Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout Filtering on Observable Array

I've started learning Knockout and I'm having some trouble filtering an observable array on a button click and displaying the results.

This is my model:

function Product(data) {          this.id = data.id;     this.name = data.name;     this.price = data.price;     this.description = data.desc;     this.image = data.image;     this.genre = data.genre;     this.show = data.show;     this.offer_desc = data.offer_desc;     this.offer_id = data.offer_id; }  function ProductModel() {     var self = this;     self.products = ko.observableArray([]);      $.getJSON('../PHP/Utilities.php?json=true', function(json) {        var mappedProducts = $.map(json, function(item) { return new Product(item) });        self.products(mappedProducts);     });      self.filterProducts = ko.computed(function(genre) {         if(typeof genre === 'undefined') {             return self.products(); //initial load when no genre filter is specified         } else {             return ko.utils.arrayFilter(self.products(), function(prod) {                 return prod.genre = genre;             });         }     }); }  ko.applyBindings(new ProductModel()); 

This is the html:

<div data-bind="foreach: filterProducts">     <div class="row">         <div class="col-md-2">         <img data-bind="attr:{src: '../images/' + image, alt: name}" />         </div>         <div class="col-md-2" data-bind="text: name"></div>         <div class="col-md-1" data-bind="text: price"></div>         <div class="col-md-3" data-bind="text: description"></div>         <div class="col-md-1" data-bind='text: offer_id'>                           <div class="col-md-2" data-bind="text: genre"></div>         <div class="col-md-1" data-bind="text: show"></div>     </div> </div> 

I'm also not sure how to bind a click function to filter the products on genre. I thought something like this...but it doesn't work

<button data-bind="click: filter('1')"> Filter </button>  self.filter = function(genre) {     self.filterProducts(genre); } 
like image 429
SkelDave Avatar asked Dec 31 '13 12:12

SkelDave


People also ask

Is Knockout observable?

An observable is useful in various scenarios where we are displaying or editing multiple values and require repeated sections of the UI to appear and disappear as items are inserted and deleted. The main advantage of KO is that it updates our UI automatically when the view model changes.

How do I sort Knockout observable array?

Description. The KnockoutJS Observable sort() method sorts all items in the array. By default, items are sorted in an ascending order. For sorting an array in a descending order, use reverse() method on sorted array.

How do you remove data from an observable array?

shift() — Removes the first value from the array and returns it. reverse() — Reverses the order of the array and returns the observableArray (not the underlying array). sort() — Sorts the array contents and returns the observableArray .


2 Answers

You cannot have a function with parameters inside a ko.computed.

What you need is store the current filter in a new property and use that in your computed

function ProductModel() {     var self = this;     self.products = ko.observableArray([]);      self.currentFilter = ko.observable(); // property to store the filter      //...      self.filterProducts = ko.computed(function() {         if(!self.currentFilter()) {             return self.products();          } else {             return ko.utils.arrayFilter(self.products(), function(prod) {                 return prod.genre == self.currentFilter();             });         }     }); } 

And in your click handler just set the current filter:

<button data-bind="click: function() { filter('1') }"> Filter </button>  self.filter = function(genre) {     self.currentFilter(genre); } 

Demo JSFiddle

Note the function() { } in needed if you want to pass additional arguments a in click binding (see also in the documentation), otherwise Knockout would execute your function when it parses the binding and not when you click on the button.

like image 69
nemesv Avatar answered Oct 04 '22 20:10

nemesv


You might want to have a look at Knockout Projections plugin from the author of original knockout. It has performance advantages in scenarios with large collections. See blogpost for more details.

self.filterProducts = self.products.filter(function(prod) {     return !self.currentFilter() || prod.genre == self.currentFilter(); }); 
like image 30
altso Avatar answered Oct 04 '22 20:10

altso