Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockoutjs Array Filter

Tags:

knockout.js

Can someone please help me with this?

How do I get the multi select list to have unique countries? Also, how do I filter the records based on the items selected in the multi select?

Here's the code in JsFiddle http://jsfiddle.net/B2xcv/

Please help.

Thanks.

HTML

   <div class='liveExample'> 
   <p style="color:red">How do I make this list unique?</p>
    <p>
    <select data-bind="options: people,optionsText: 'country', selectedOptions: selectedCountries" size="5" multiple="true" style="width:150px"></select>
  </p>
  <p style="color:red">And how do I filter the records below based on the selected items above? (multiple select)</p>
   <table style="width:300px">
     <thead>
       <th>Name</th>
       <th>Location</th>
     </thead>
    <tbody data-bind="foreach: people">
        <tr>
          <td>
                <span data-bind="text: name"> </span> 
          </td>
            <td>
                <span data-bind="text: country"> </span> 
          </td>
        </tr>
    </tbody>
  </table>

</div>

KnockoutJS

// Define a "Person" class that tracks its own name and children, and has a method to add a new child
var Person = function(name, country) {
    this.name = name;
    this.country = country;
}

// The view model is an abstract description of the state of the UI, but without any knowledge of the UI technology (HTML)
var viewModel = {
    people: [
        new Person("Annabelle", "UK"),
        new Person("Bertie", "UK"),
       new Person("Bertie", "USA"),
        new Person("Ali", "Bangladesh"),
        new Person("Patel", "India"),
      new Person("Sweatha", "India"),
       new Person("Minto", "India"),
        ],
  selectedCountries: ko.observableArray()
};

ko.applyBindings(viewModel);
like image 778
Huzzi Avatar asked Jan 15 '23 12:01

Huzzi


2 Answers

OK- the easiest way to make this work is to create variables to represent the unique countries and the filtered version of the people array.

If people was an observableArray, then you would likely want the uniqueCountries variable to be a computed. If it is not observable, then you could just calculate it directly.

Here is a sample:

viewModel.uniqueCountries = ko.computed(function() {
      var people = viewModel.people(),
          index = {},
          uniqueCountries= [],
          length = people.length,
          i, country;

      for (i = 0; i < length; i++) {
          country = people[i].country;      
          if (!index[country]) {
               index[country] = true;
               uniqueCountries.push(country);
          }
      } 

      return uniqueCountries;
});

The main idea is to just take one loop through all of the people and build up an array of the unique countries. To do this, I just use an "index" object, so that I can easily check if we have already included this country (without looping on the list that we are building). Then, you can return the unique countries as an array and bind against it in your select.

Next, you need to create a computed observable to represent the filtered version of people. Here is a sample:

viewModel.filteredPeople = ko.computed(function() {
  var selected = viewModel.selectedCountries() || [],
      index = {};

  //build an index, so we can avoid looping each time
  ko.utils.arrayForEach(selected, function(country) {
      index[country] = true;
  });

  return ko.utils.arrayFilter(viewModel.people(), function(person) {
      return index[person.country];
  });
});

Again I built an index, since you can select multiple countries. This just makes it easy to check if the person's country is one of the selected countries. ko.utils.arrayFilter will let us run a function on each item in an array and return a new array containing any items where the function returned a "truthy" value. Again I assumed that people was an observableArray that you might be adding/removing items from.

Here is the updated sample: http://jsfiddle.net/rniemeyer/xsfRR/

like image 64
RP Niemeyer Avatar answered Jan 22 '23 02:01

RP Niemeyer


I know it might a little late but for anyone interested i did it this way using ko built in functions:

 var viewModel = {
    people: [
        new Person("Annabelle", "UK"),
        new Person("Bertie", "UK"),
       new Person("Bertie", "USA"),
        new Person("Ali", "Bangladesh"),
        new Person("Patel", "India"),
      new Person("Sweatha", "India"),
       new Person("Minto", "India"),
        ],
      selectedCountries: ko.observableArray(),
      uniqueCountries: function(){
            var countries =ko.utils.arrayMap(viewModel.people,function(person){
                return person.country;
            });
            return ko.utils.arrayGetDistinctValues(countries);
        },
        filteredRecords: function(){
            return ko.utils.arrayFilter(viewModel.people,function(person){
               return person.country == viewModel.selectedCountries(); 
            });
        }
};

ko.applyBindings(viewModel);

and the html

  <div class='liveExample'> 
   <p style="color:red">How do I make this list unique?</p>
    <p>
    <select data-bind="options: uniqueCountries(), selectedOptions: selectedCountries" size="5" multiple="true" style="width:150px"></select>
  </p>
  <p style="color:red">And how do I filter the records below based on the selected items above? (multiple select)</p>
   <table style="width:300px">
     <thead>
       <th>Name</th>
       <th>Location</th>
     </thead>
    <tbody data-bind="foreach: filteredRecords()">
        <tr>
          <td>
                <span data-bind="text: name"> </span> 
          </td>
            <td>
                <span data-bind="text: country"> </span> 
          </td>
        </tr>
    </tbody>
  </table>

</div>

and here in jsfiddle http://jsfiddle.net/geomorillo/n5JFL/1/

like image 26
Geomorillo Avatar answered Jan 22 '23 02:01

Geomorillo