it-swarm.com.de

Knockout-Filterung für beobachtbares Array

Ich habe angefangen, Knockout zu lernen und habe Probleme beim Filtern eines beobachtbaren Arrays mit einem Klick auf eine Schaltfläche und beim Anzeigen der Ergebnisse.

Das ist mein Modell:

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

Dies ist das 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>

Ich bin mir auch nicht sicher, wie ich eine Klickfunktion binden soll, um die Produkte nach Genre zu filtern. Ich dachte so etwas ... aber es funktioniert nicht

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

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

Sie können keine Funktion mit Parametern innerhalb eines ko.computed Haben.

Was Sie brauchen, ist, den aktuellen Filter in einer neuen Eigenschaft zu speichern und diesen in Ihrem berechneten zu verwenden

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

Und in Ihrem click -Handler setzen Sie einfach den aktuellen Filter:

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

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

Demo JSFiddle

Beachten Sie das function() { } in, wenn Sie zusätzliche Argumente in click -Bindung übergeben möchten (siehe auch in der Dokumentation ), andernfalls würde Knockout Ihre Funktion ausführen, wenn es analysiert die Bindung und nicht, wenn Sie auf die Schaltfläche klicken.

53
nemesv

Zuerst missverstehst du/verwendest du für computed Observables. Von KnockoutJS-Dokumentation :

hierbei handelt es sich um Funktionen, die von einer oder mehreren anderen Observablen abhängig sind und automatisch aktualisiert werden, wenn sich eine dieser Abhängigkeiten ändert.

Ihre berechnete beobachtbare filterProducts hängt von dem beobachtbaren Array products ab, das Sie nicht ändern . Sie lesen nur den Wert . Es gibt also nichts zu benachrichtigen, dass die filterProducts erneut ausgewertet werden soll.

Was wäre also eine schnelle, einfache Lösung?

  1. Definieren Sie ein neues beobachtbares Objekt filteredGenre, von dem Ihre filterProducts abhängt.
  2. Ändern Sie filterProducts so, dass es den Wert von filteredGenre prüft und basierend darauf gefilterte Produkte zurückgibt.
  3. Ändern Sie die Funktion filter so, dass sie beim Abrufen einer neuen genre die Funktion filteredGenre ändert, was zu einer Neubewertung der berechneten filterProducts führen würde.

Ich hoffe du hast die Idee.

5
ebram khalil

Vielleicht möchten Sie sich das Knockout-Projektionen -Plugin des Autors des ursprünglichen Knockouts ansehen. In Szenarien mit großen Sammlungen hat es Leistungsvorteile. Siehe Blogpost für weitere Details.

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