Ronny vdb
Ronny vdb

Reputation: 2474

knockout js table filtering broken on newer versions

I am trying to implement that functionality on this demo: http://opensoul.org/2011/06/23/live-search-with-knockoutjs/

I managed to get everything working, but the demo uses a very old version of knockout. When I upgrade the knockout version the functionality breaks.

This is my updated code:

$(function() {
  var assets = [
    {id: "1", poster: "Pic010.jpg", name: "Mike", category: "category1", type: "Movie", popup: "1" },
    {id: "2", poster: "Pic06.jpg", name: "James", category: "category2", type: "Movie", popup: "2" },
    {id: "3", poster: "Pic04.jpg", name: "John", category: "category1", type: "Pop-up", popup: "3" },
    {id: "4", poster: "Pic07.jpg", name: "Bob", category: "category2", type: "Pop-up", popup: "4" },
    {id: "5", poster: "Pic011.jpg", name: "Mary", category: "category3", type: "Promo", popup: "5" }
  ];

  var viewModel = {
    assets: ko.observableArray(assets),

    query: ko.observable(''),

    search: function(value) {
      viewModel.assets.removeAll();
      for(var x in assets) {
        if(assets[x].name.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
          viewModel.assets.push(assets[x]);
        }
      }
    }
  };

  viewModel.query.subscribe(viewModel.search);

  ko.applyBindings(viewModel);
});

The display:

    <form action="#">
      <input class="form-control" placeholder="Search…" type="search" name="q" data-bind="value: query, valueUpdate: 'keyup'" autocomplete="off">
    </form>

  <div class="content">
    <table>
      <tbody data-bind="foreach:assets">
        <tr>
    <td style="vertical-align: middle" data-bind="text: id">&nbsp;</td>
    <td><img data-bind="attr:{ src: '/manager/files/' + poster }" height="100px" /></td>
    <td style="vertical-align: middle" data-bind="text: name"></td>
    <td style="vertical-align: middle" data-bind="text: category"></td>
    <td style="vertical-align: middle" data-bind="text: type"></td>
    <td style="vertical-align: middle" data-bind="text: popup"></td>
    <td class="actions" style="vertical-align: middle">
        <a href="/Assets/edit/5" data-bind="attr:{ href: '/Assets/edit/' + id }"><i class="fa fa-pencil-square-o"></i></a>
        <form data-bind="attr:{ action: '/Assets/delete/' + id, name: 'delete_' + id, id: 'delete_'+ id }" style="display:none;" method="post"><input type="hidden" name="_method" value="POST"></form>
        <a href="#" ><i class="fa fa-trash-o"></i></a>
    </td>
</tr>
    </tbody>
        </table>
  </div>

here are examples, working with Knockout version 1.21: http://jsfiddle.net/7ZLdk/1/ example not working with version 3.0: http://jsfiddle.net/7ZLdk/2/

Upvotes: 0

Views: 62

Answers (1)

nemesv
nemesv

Reputation: 139778

This behavior has been changed back in 2011: Updated remove and removeAll to modify their underlying arrays rather han creating new arrays

So now when you call removeAll on an ko.observableArray it removes the items form the underlying arrays, so in your case it empties your original assets array.

A quick fix would be to clone the array when assigning to your ko.observableArray:

assets: ko.observableArray(assets.slice(0)),

Demo JSFiddle.

A better and more modern solution would be to use a ko.computed property and the arrayFilter helper method to do the filtering:

 assets: ko.computed(function () {
     if (!viewModel.query())
         return assets;
     return ko.utils.arrayFilter(assets, function(item) {
        return item.name.toLowerCase().indexOf(viewModel.query().toLowerCase()) >= 0;             
     });
 }, null, {deferEvaluation: true})

Demo JSFiddle.

Upvotes: 3

Related Questions