web-dev-qa-db-fra.com

Filtrage de masquage sur un tableau observable

J'ai commencé à apprendre Knockout et je ne parviens pas à filtrer un tableau observable en cliquant sur un bouton et à afficher les résultats.

Ceci est mon modèle:

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

C'est le 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>

Je ne sais pas non plus comment lier une fonction de clic pour filtrer les produits par genre. Je pensais que quelque chose comme ça ... mais ça ne marche pas

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

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

Vous ne pouvez pas avoir de fonction avec des paramètres dans un ko.computed.

Ce dont vous avez besoin est de stocker le filtre actuel dans une nouvelle propriété et de l’utiliser dans votre calcul.

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

Et dans votre gestionnaire click, définissez simplement le filtre actuel:

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

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

Démo JSFiddle

Notez que function() { }in est nécessaire si vous souhaitez passer des arguments supplémentaires a dans click binding (voir aussi dans la documentation ), sinon Knockout exécuterait votre fonction analyse la liaison et non lorsque vous cliquez sur le bouton.

53
nemesv

D'abord, vous comprenez mal/utilisez pour computed Observables. De documentation KnockoutJS :

ce sont des fonctions qui dépendent d'un ou plusieurs autres observables, et se mettront automatiquement à jour chaque fois que l'une de ces dépendances changera.

Votre calculable observable filterProducts dépend du tableau observable products que vous ne modifiez pas , vous venez de lire sa valeur. Donc, il n'y a rien à notifier à filterProducts d'être réévalué.

Alors, quelle serait solution rapide rapide?

  1. Définissez un nouvel objet observable filteredGenre sur lequel votre filterProducts dépendra.
  2. Modifiez filterProducts pour qu'il vérifie la valeur de filteredGenre et renvoie les produits filtrés.
  3. Change la fonction filter de sorte que, lorsqu’elle récupère genre, elle change filteredGenre, ce qui entraînerait une réévaluation du calcul filterProducts

J'espère que tu as eu l'idée.

5
ebram khalil

Vous voudrez peut-être jeter un oeil à Knockout Projections plug-in de l'auteur du knockout original. Il présente des avantages en termes de performances dans les scénarios avec des collections volumineuses . Voir blogpost pour plus de détails.

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