Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding category rows based on matching item properties

I have a sorted, static list to display with KO, and wanted to show category headers whenever the category changed (since the list is sorted by category). I'm still genning up on KO, is this the "KO" way to do this, or is there a better approach? In particular the syntax to access the previous item in the list is a bit hairy, which makes me suspect I'm missing a feature that would improve this. :-)

Live Copy | Source

HTML:

<table>
  <tbody data-bind="foreach: items">
    <!-- ko if: $index() === 0 || $parent.items()[$index() - 1].category() !== category() -->
    <tr class="category">
      <td colspan="2" data-bind="text: category"></td>
    </tr>
    <!-- /ko -->
    <tr>
      <td data-bind="text: item"></td>
      <td class="num" data-bind="text: quantity"></td>
    </tr>
  </tbody>
</table>

JavaScript: (Obviously this is just a quick and dirty VM for the example)

function Item(category, item, quantity) {
    this.category = ko.observable(category);
    this.item = ko.observable(item);
    this.quantity = ko.observable(quantity);
}

var vm = {
    items: ko.observableArray([
        new Item("Fruit", "Apples", 27),
        new Item("Fruit", "Oranges", 17),
        new Item("Fruit", "Kiwis", 3),
        new Item("Vegetables", "Celery", 16),
        new Item("Vegetables", "Carrots", 72),
        new Item("Sundries", "Toothpaste", 10),
        new Item("Sundries", "Washing-up liquid", 8)
    ])
};
ko.applyBindings(vm, document.body);

Result: (there's some trivial CSS that isn't relevant)

Table with category rows

like image 644
T.J. Crowder Avatar asked Jan 21 '14 12:01

T.J. Crowder


1 Answers

If you modify your observable array and construct it to contain an array of items with associated quantities you can do the following:

JS:

function Item(category, itemList) {
    this.category = ko.observable(category);
    this.itemList = ko.observableArray(itemList);

}

var vm = {
    items: ko.observableArray([
        new Item("Fruit", [{"item": "Apples", "qty": 27 }, 
                           {"item": "Oranges", "qty": 17}, 
                           {"item": "Kiwis", "qty": 3}]),              
        new Item("Vegetables", [{"item": "Celery", "qty": 16},
                                {"item": "Carrots", "qty": 72}]),      
        new Item("Sundries", [{"item": "Toothpaste", "qty": 10},
                              {"item": "Washing-up liquid", "qty": 8}]),    
    ])
};

ko.applyBindings(vm, document.body);

HTML:

<table>
  <tbody data-bind="foreach: items">    
    <tr class="category">
      <td colspan="2" data-bind="text: category"></td>
    </tr>
    <!-- ko foreach: itemList -->
        <tr>
          <td data-bind="text: item"></td>
          <td class="num" data-bind="text: qty"></td>
        </tr>
    <!-- /ko -->    
  </tbody>
</table>

See JSFiddle here: http://jsfiddle.net/y4yPv/2/

like image 162
Tanner Avatar answered Sep 19 '22 02:09

Tanner