sobota, 22 września 2012

[HTML|JS|CSS] KnockoutJS : Kolekcje

Data binding do kolekcji elementów jest czymś powszechnie stosowanym. Potrzeba wyświetlania zestawów danych na własne potrzeba pojawia się przy każdym większym projekcie. KnockoutJS daje możliwość prostego bindingu przy użyciu atrybutów oraz odpowiednich typów w ViewModelu.

Wyświetlanie kolekcji

Binding do kolekcji tworzy się w widoku za pomocą dyrektywy foreach, a po dwukropku podajemy nazwę kolekcji z ViewModelu. Jeżeli w przykładzie poniżej people jest kolekcją obiektów, to name, surname oraz added są kolejnymi properties takich obiektów.

<div data-bind="foreach: people">
 <div>
      <strong data-bind="text: name"></strong>
      <strong data-bind="text : surname"></strong>
      <strong data-bind="text : added"></strong> 
 </div>
</div>

W ViewModelu kolekcja musi być typu ko.observableArray, co zapewni śledzenie zmian. Warto przygotować sobie również funkcję - konstruktor do szybkiego tworzenia obiektów.

//funkcja konstruktora dla osoby

function Person(name, surname){
 var self = this;
 self.name = name; 
 self.surname = surname;
 self.added = new Date();
}

function AppViewModel() {

    // pozostala czesc inicjalizacji
    
    this.people = ko.observableArray([
     new Person("John","Doe"),
     new Person("Jack","The Ripper")
     ]);   
}

ko.applyBindings(new AppViewModel());

Powyższy kod wyświetli dane sformatowane do naszych potrzeb.

Dodawanie elementów

Jeżeli zajdzie potrzeba dynamicznego dodawania do kolekcji elementów, można stworzyć odpowiednią funkcję wewnątrz ViewModelu.
Od strony widoku można dodać przycisk z bindingiem do zdarzenia click.

<button data-bind="click: addPerson">Add</button>

W ViewModelu należy z kolei dodać funkcję, w której zwiększamy kolekcję używając funkcji push. Wszystkie zmiany w kolekcji spowodują automatyczne zmiany w widoku.

function AppViewModel() {
    //pozostala czesc inicjalizacji
    var that = this;
    this.addPerson = function(){
     that.people.push(new Person(that.firstName, that.lastName));      
    };
}

Edytowanie elementów

Dzięki śledzeniu zależności, każda zmiana wartości automatycznie odświeży kolekcję. Aby edytować elementy za pomocą elementu select, należy nieco zmodyfikować widok:

<div data-bind="foreach: people">
   <div>
 <!--pozostale elementy-->
 <select data-bind="options: $root.jobs, value: job, optionsText: 'name'"></select>
 <strong data-bind="text : job().salary"></strong>
   </div>
</div>

Obiekt select dostaje informacje o tym, z której kolekcji ma pobierać listę wartości (tablica jobs z ViewModelu), do którego property elementu z kolekcji people przypisać wartość (obserwowalny element job) oraz które property z elementów jobs wyświetlać jako opcję do wyboru.

Tymczasem po stronie ViewModelu:

function Person(name, surname,job){
 var self = this;
 //pozostale properties
 self.job = ko.observable(job);
}

function AppViewModel() {    

    this.jobs = [
    {name: "manger", salary : 6000},
    {name: "tester", salary : 2000},
    {name: "developer", salary : 4000}
    ];

    var that = this;
}

Jak widzimy jobs to kolekcja obiektów. Wyświetlamy tylko property name, ale po wybraniu obiektu mamy też dostęp do property salary. Możemy je wyświetlać w widoku za pomocą instrukcji data-bind="text : job().salary", jako odwołanie do property salary z obiektu job, który jest property elementu kolekcji people.

Usuwanie elementów

Po stronie widoku należy dodać element powodujący usunięcie elementu np. poprzez click.

<div data-bind="foreach: people">
 <div>
 <!--pozostale elementy -->
 <a data-bind="click: $root.removePerson">Remove item</a>
 </div>
</div>

Jako lokalizację funkcji podaje się $root, czyli najbardziej zewnętrzny ViewModel.


function AppViewModel() {
    var that = this;

    this.removePerson = function(person){
     that.people.remove(person);
    }
}


Agregacja wartości:

Pisanie swoich funkcji wyliczających pewne wartości na całej kolekcji staje się proste dzięki mechanizmowi śledzenie zależności. Gdy chcemy posumować wszystkie wartości i zbindować tę sumę do osobnego elementu, wartość zostanie odświeżona automatycznie przy każdej zmianie na kolekcji. Możemy także zbindować się do właściwości visible i wyświetlać dany element w zależności od spełnienia pewnego warunku logicznego. Na przykład:

<div data-bind="visible: totalSalary()>0">
 <p>Total salary:</p>
 <strong data-bind="text: totalSalary"></strong>
</div>

gdzie total salary jest typem ko.computed w ViewModelu.


that.totalSalary = ko.computed(function() {
   var total = 0;
   for (var i = 0; i < that.people().length; i++)
       total += that.people()[i].job().salary;
   return total;
});

Brak komentarzy:

Prześlij komentarz