Unfortunately, I have some discouraging news to share with you. Html tables follow a strict row-based order, which can make organizing your data in this manner quite challenging.
You are faced with two potential solutions:
Option A: Restructure Your Data
You could create a specialized View Model for a "Feature-Comparison" and reorganize your data accordingly. By transposing your data in this way, you can then utilize a foreach
loop within your tbody
to iterate through these "featureComparisons". Here is an example of how this can be implemented in code:
var inputData = [
{ name: "First product", rating: 4.5, price: "$3.50", color: "Gray" },
{ name: "Another product", rating: 3.5, price: "$2.95", color: "Yellow" }
];
function FeatComparison(comparisonName, comparedValues) {
this.comparisonName = comparisonName;
this.comparedValues = comparedValues;
}
var vm = { comparisons: ko.observableArray([]) };
// Alternatively, you can explicitly receive a list of comparable properties from the backend.
// In this example, we iterate over the first product.
for (var prop in inputData[0]) {
if (inputData[0].hasOwnProperty(prop)) {
var vals = inputData.map(function(i) { return i[prop]; });
vm.comparisons.push(new FeatComparison(prop, vals));
}
}
ko.applyBindings(vm);
td { background: #eee; padding: 5px 10px; }
tr:first-child, td:first-child { font-weight: bold; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table><tbody data-bind="foreach: comparisons">
<tr>
<td data-bind="text: comparisonName"></td>
<!-- ko foreach: comparedValues -->
<td data-bind="text: $data"></td>
<!-- /ko -->
</tr>
</tbody></table>
This particular option works best for scenarios where you need to facilitate detailed comparisons, such as comparing multiple products or allowing users to customize which features are included in the comparison.
Option B: Embrace the Challenge (due to lack of a more fitting title)
If restructuring your data seems like too much effort, you can opt to leave it as is and simultaneously iterate through both objects using foreach
. The following code demonstrates how this can be done:
var inputData = [
{ name: "First product", rating: 4.5, price: "$3.50", color: "Gray" },
{ name: "Another product", rating: 3.5, price: "$2.95", color: "Yellow" }
];
function RootVm(data) {
var self = this;
this.products = ko.observableArray(data);
this.keys = ko.computed(function() {
if (self.products().length === 0) return [];
return Object.keys(self.products()[0]);
});
}
ko.applyBindings(new RootVm(inputData));
td { background: #eee; padding: 5px 10px; }
tr:first-child, td:first-child { font-weight: bold; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<table><tbody data-bind="foreach: keys">
<tr>
<td data-bind="text: $data"></td>
<!-- ko foreach: $root.products -->
<td data-bind="text: $data[$parent]"></td>
<!-- /ko -->
</tr>
</tbody></table>