Gabe O'Leary February 2016

Rendering a side by side comparison using knockout

I'm working on a project where I would like to be able to display several items side by side for comparison.

I'd like to have the comparison displayed similarly to the way in which amazon does, with each item being represented by a vertical column with all of the info about the item in that column:

enter image description here

I'm currently using Knockout, and have an Observable array with the items I would like to compare. I would like to use the foreach binding to do the job and my problem stems from the fact that table's have all cells for a specific row contained in the same element which makes it difficult to use:

<tr>
  <td>Rating</td>
  <td> 4.5 (data from item 1)</td>
  <td> 3.5 (data from item 2)</td>
</tr>
<tr>
  <td>price</td>
  <td>$2.3 (data from item 1) </td>
  <td>$3.5 (data from item 2) </td>
</td>

If I wanted each item to be contained in a row this would not be a problem. Anyone have any recommendations for how to tackle this issue since the data for different items has to exists in different (overlapping) parts of the document.

Answers


Jeroen February 2016

I have bad news for you. Html tables are strictly ordered in rows, and this kind of layout for your data can be a bit frustrating.

You have two options, basically:

Option A: Transpose your data

You could introduce a specific View Model for a "Feature-Comparison" and transpose your data to that. You can then do a foreach on your tbody iterating through those "featureComparisons". In code, here's what I mean:

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([]) };

// You could also explicitly send a list of comparable properties from back-end.
// For this example we iterate 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 --&g 

Post Status

Asked in February 2016
Viewed 2,602 times
Voted 13
Answered 1 times

Search




Leave an answer