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);
}
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.
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?
filteredGenre
sur lequel votre filterProducts
dépendra.filterProducts
pour qu'il vérifie la valeur de filteredGenre
et renvoie les produits filtrés.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.
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();
});