web-dev-qa-db-ja.com

観測可能な配列のノックアウトフィルタリング

Knockoutの学習を開始しましたが、ボタンクリックで観測可能な配列をフィルタリングして結果を表示するのに問題があります。

これは私のモデルです:

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());

これは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>

また、クリック機能をバインドしてジャンルの製品をフィルタリングする方法もわかりません。私はこのようなことを考えました...しかし、それは動作しません

<button data-bind="click: filter('1')"> Filter </button>

self.filter = function(genre) {
    self.filterProducts(genre);
}
41
SkelDave

_ko.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();
            });
        }
    });
}
_

そして、clickハンドラーで現在のフィルターを設定するだけです:

_<button data-bind="click: function() { filter('1') }"> Filter </button>

self.filter = function(genre) {
    self.currentFilter(genre);
}
_

デモ JSFiddle

clickバインディングで追加の引数aを渡したい場合は、必要なfunction() { }に注意してください( documentation も参照)。そうでない場合、Knockoutは関数を実行しますボタンをクリックしたときではなく、バインディングを解析します。

53
nemesv

まず、computed ObservablesKnockoutJSドキュメント から:

これらは、1つ以上の他のオブザーバブルに依存する関数であり、これらの依存関係のいずれかが変更されるたびに自動的に更新されます。

計算されたオブザーバブルfilterProductsは、オブザーバブル配列productsに依存しますが、これは変化しません。値を読み取るだけです。したがって、filterProductsを再評価するよう通知することはありません。

それでは、簡単な簡単な修正は何でしょうか?

  1. filteredGenreが依存する新しいオブザーバブルオブジェクトfilterProductsを定義します。
  2. filterProductsの値を確認し、それに基づいてフィルターされた製品を返すように、filteredGenreを変更します。
  3. filter関数を変更して、新しいgenreを取得したときにfilteredGenreを変更し、計算されたfilterProductsを再評価するようにします。

私はあなたがアイデアを得たことを願っています。

5
ebram khalil

オリジナルのノックアウトの作者が作成した Knockout Projections プラグインをご覧ください。 大規模なコレクションを使用したシナリオでパフォーマンス上の利点があります。詳細については、 blogpost を参照してください。

self.filterProducts = self.products.filter(function(prod) {
    return !self.currentFilter() || prod.genre == self.currentFilter();
});
5
altso