0
votes

I have a knockout observable array having a few objects in it. Now each of the object has a property called group. I would like to know if we can bind the items of this array to different elements depending on their groups.

For example, An observable array has 5 items in it. In bracket I'm showing their groups.

  • Item1 (group1)

  • Item2 (group1)

  • Item3 (group2)

  • Item4 (group2)

  • Item5 (group3)

We can bind all 5 items to a table or UL but what I want is to be able to have 3 different tables/UL because 3 distinct groups are identified as group1, group2 and group3.

So, first table/UL should bind Item1 & Item2.

Second table/ul should bind Item3 & Item4.

Third table/UL should bind Item5.

Considering the array populates dynamically so we can not keep the fixed number of tables/UL elements to bind.

Thanks!

1

1 Answers

1
votes

Say your items look like this:

var Item = function(name, group) {
  this.name = name;
  this.group = group;
}

And they're in an observable array:

var items = ko.observableArray([
  new Item("Item1", "group1"),
  new Item(/* ... */),
  /* et cetera */
]);

To create an object with all items sorted by group, create a computed:

var itemsByGroup = ko.computed(function() {
  return items().reduce(function(result, item) {
    if (!result[item.group]) {
      result[item.group] = [];
    }

    result[item.group].push(item);

    return result;
  }, {});
});

You need to create a computed 2-dimensional array to use the knockout foreach binding.

var itemGroups = ko.computed(function() {
  return Object.keys(itemGroups())
    .map(function(key) { return itemGroups()[key]});
});

Now, you can create your lists using HTML markup like so:

<ul data-bind="foreach: itemGroups">
  <li>
    <ul data-bind="foreach: $data">
      <li data-bind="text: name"></li>
    </ul
  </li>
<ul>

If you want to add group labels and such, you can return an object with a label property and the items in an items array. Experiment with it using the fiddle below:

var Item = function(name, group) {
  this.name = name;
  this.group = group;
}

var items = ko.observableArray([
  new Item("1", "A"),
  new Item("2", "A"),
  new Item("3", "B"),
  new Item("4", "C")
]);

var itemsByGroup = ko.computed(function() {
  return items().reduce(function(result, item) {
    if (!result[item.group]) {
      result[item.group] = [];
    }

    result[item.group].push(item);

    return result;
  }, {});
});

var groupedArray = ko.computed(function() {
  var grouped = itemsByGroup();
  return Object.keys(grouped)
    .map(function(key) {
      return grouped[key]
    });
});

ko.applyBindings({
  groupedArray: groupedArray
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>

<ul data-bind="foreach: groupedArray">
      <li>
        <ul data-bind="foreach: $data">
          <li data-bind="text: name"></li>
        </ul
      </li>
    <ul>