Подтвердить что ты не робот

Фиксация нокаута на наблюдаемом массиве

Я начал изучать нокаут, и у меня возникли проблемы с фильтрацией наблюдаемого массива нажатием кнопки и отображением результатов.

Это моя модель:

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);
}
4b9b3361

Ответ 1

У вас не может быть функция с параметрами внутри 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

Обратите внимание на function() { } в случае необходимости, если вы хотите передать дополнительные аргументы a в привязку click (см. также в documentation), иначе Knockout будет выполняйте свою функцию, когда она анализирует привязку, а не когда вы нажимаете кнопку.

Ответ 2

Сначала вы неправильно понимаете/используете для computed Observables. Из Документация KnockoutJS:

это функции, зависящие от одного или нескольких других наблюдаемых, и будет автоматически обновляться всякий раз, когда любой из этих изменения зависимостей.

Вычисленный наблюдаемый filterProducts зависит от наблюдаемого массива products, который вы не меняете, вы просто прочитали его значение. Таким образом, нет ничего, чтобы уведомить filterProducts для переоценки.

Итак, что было бы быстрым простым исправлением?

  • Определите новый наблюдаемый объект filteredGenre, от которого зависит ваш filterProducts.
  • Измените filterProducts так, чтобы он проверял значение filteredGenre и на основании этого возвращал отфильтрованные продукты.
  • Измените filter функцию, чтобы при ее получении genre она меняла filteredGenre, что привело бы к переоценке вычисленного filterProducts

Надеюсь, у вас есть идея.

Ответ 3

Возможно, вы захотите посмотреть плагин для нокаутов от автора оригинального нокаута. Он имеет преимущества производительности в сценариях с большими коллекциями. Подробнее см. blogpost.

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