Przetwarzanie danych za pomocą Bindings

W poprzednim wpisie przedstawiłem jak tworzyć widok dla ViewModel, czyli jak stworzyć szablon HTML dla obiektu JavaScript. W tym wpisie przedstawie jak płynnie operować danymi. Na przykład pętla loop która przetwarza listę danych i wyświetla je bądź nie, na podstawie wcześniej określonych warunkach. Widzieliśmy jak działa bindowanie foreach, czyli przejście pętli przez observable array. Knockout.js posiada jeszcze dwa logiczne bindowania takie jak if i ifnot

Zademonstruje możliwości płynnego operowania danymi w Knockout.js za pomocą wyżej wymienionych atrybutów i innych oraz wcześniej już utworzonego koszyka w poprzednich wpisach z serii Knockout.js.

Bindowanie foreach

Przyjrzyjmy się bliżej naszej pętli foreach wykorzystanej w poprzednim wpisie.



<tbody data-bind='foreach: basket'>
<tr>
  <td data-bind='text: name'></td>
  <td data-bind='text: price'></td>
  <td><button data-bind='click: $root.removeProduct'>Remove</button></td>
</tr>
</tbody>

Kiedy Knockout.js napotyka foreach w atrybucie data-bind, wtedy pętla iteruje przejścia przez nasz koszyk wyciągając każdy element znajdujący się w obserwowalnej tablicy, które są zbindowane razem z kontekstem. Knockout.js zarządza zbindowanym kontekstem dzięki któremu zakres dostępu do elementów w liście które mają właściwości jest bezproblemowy. Dlatego w pętli podaliśmy same nazwy właściwości jak name, price bez użycia referencji do instancji Product.

Praca z bindowanym kontekstem

Biorąc każdy element z tablicy jako nowy bindowany kontekst, powoduje to wygodny sposób tworzenia pętli, ale takie zachowanie powoduję, że nie można odwołać się do obiektów po za zakresem pętli. Dlatego Knockout.js stworzył kilka specjalnych właściwości które są dostępne w każdym bindowanym kontekście. Warto wiedzieć, wszystkie te nowe właściwości które zaraz przedstawię są dostępne tylko z poziomu widoku, nie z ViewModel.

Właściwość $root

Kontekst $root zawsze odnosi się do najwyższego poziomu ViewModel. Bez względnie na zmiany zachodzące w pętli czy innym zakresie. Po prost $root widzi wszystko w ViewModel. Jak widzieliśmy zachowanie $root w poprzednim przykładzie, bez problemu można było z niego skorzystać wewnątrz pętli, czyli możemy manipulować metodami ViewModel za pomocą $root.

Właściwość $data

Właściwość $data która jest zbindowana w kontekście, odnosi się do aktualnego kontekstu ViewModel’owego obiektu. Działa na zasadzie this w obiekcie JavaScript. Na przykład, wewnątrz naszej pętli foreach: basket, $data odwołuję się do aktualnej listy. Jak na przykładzie poniżej, kod działa również dobrze, bez użycia $data:

<td data-bind='text: $data.name'></th>
<td data-bind='text: $data.price'></th>

Może wydawać się to dość banalny przykład, ale jest niezbędny dla iteracji przez tablice zawierającą wartości atomowe, łańcuchy znaków czy liczby. Na przykład można przechować listę składającą się z łańcucha znaków reprezentującą tagi dla każdego produktu:

 
function Product(name, price, tags){
 this.name = ko.observable(name);
 this.price = ko.observable(price);
 tags = typeof(tags) !== 'undefined' ? tags: [];
 this.tags = ko.observableArray(tags);
}

Wtedy, musimy dokonać definicji tagu w obiekcie products w naszym koszyku:

 
new Product("element 4", 40, ['test jest', 'jest test']);

Teraz, można zobaczyć jak zachowa się kontekst $data.

<tbody data-bind='foreach: basket'>
 <tr>
 <td data-bind='text: name'></td>
 <td data-bind='text: price'></td>
 <td>
   <ul data-bind='foreach:tags'>
     <li data-bind='text: $data'></li>
   </ul>
 </td>
 <td><button data-bind='click: $root.removeProduct'>Remove</button></td>
 </tr>
</tbody>

Wewnątrz pętli foreach: tags, Knockout.js używa zwykłego łańcucha znaków „test jest” i „jest test” jako bindowany kontekst. Ale, odkąd mamy dostęp do aktualnego łańcucha znaków zamiast jego właściwości, trzeba użyć obiektu $data.

Właściwość $index

Wewnątrz pętli foreach, właściwość $index przechowuję aktualny indeks tablicy. Jak większość rzeczy w Knockout.js, wartość $index aktualizuję się automatycznie, cokolwiek byśmy nie zrobili, dodali, usunęli element z połączonej obserwowalnej tablicy. Ta właściwość jest użyteczna, jeżeli chcemy wyświetlić indeks każdego elementu, na przykład:

<td data-bind='text: $index'></td>

Właściwość $parent

Właściwość $parent odwołuję się do rodzica obiektu ViewModel. Tej właściwości użyjemy gdy pracujemy z zagnieżdżonymi pętlami i potrzebujemy dostać się do właściwości poza pętlą. Na przykład, jeśli potrzebujemy dostępu do instancji Product z wnętrza pętli foreach: tags możemy użyć właściwości $parent:

<ul data-bind='foreach:tags'>
  <li>
    <span data-bind='text: $parent.name'></span>
    <span data-bind='text: $data'></span>
  </li>
</ul>

Rabaty na produkty

Nim pójdziemy dalej z bindowaniem, dodamy teraz rabaty do produktów w naszym koszyku. Przechodzimy do klasy Products i dodajem właściwość discount

discount = typeof(discount) !== 'undefined' ? discount : 0;
this.discount = ko.observable(discount);
this.formattedDiscount = ko.computed(function(){
    return (this.discount() * 100) + "%"; 
}, this);

Na początku tworzymy parametr discount, początkowo dajemy mu wartość 0. Wtedy, możemy stworzyć obserwowalną właściwość dla discount gdzie Knockout.js będzie mógł ją śledzić. W końcu, możemy zdefiniować computed observable która zwraca czytelną wersję rabatu.
Teraz dodajmy 20% rabatu dla pierwszej wartości w tablicy, przechodzimy do PersonViewModel.basket:

this.basket = ko.observableArray([
  new Product("element 1", 10, null, .20),
  new Product("element 2", 20),
  new Product("element 3", 30),
  new Product("element 4", 40, ['test jest', 'jest test'])
]);

Bindowanie if i ifnot

Wiązanie warunkowe if. Jeżeli parametr który zwróci prawdę(true), wtedy odpowiedzialny kontener HTML będzie wyświetlony, w przeciwnym przypadku, taki element HTML będzie usunięty z DOM. Do naszej tabeli dodajmy poniższy kod:

<td data-bind='if: discount() > 0' style='color: red'>
Zaoszczędziłeś <span data-bind='text: formattedDiscount'></span>!!!
</td>
<td><button data-bind='click: $root.removeProduct'>Remove</button></td>

Wszystko co jest wewnątrz elementu td pokaże dla elementu z tablicy rabat jeżeli jest większy od 0. W dodatku odkąd discount jest obserwowalne, Knockout.js automatycznie zarządzi aktualizację kiedy nastąpi jakakolwiek zmiana. Analogicznie ifnot z tym, że na odwrót.

Właściwość with

Bindowanie with pozwala ręcznie zadeklarować zakres w poszczególnym bloku. Spróbujmy dodać poniższy kod, na samej górze w widoku.

<p data-bind='with: featuredProduct'>
Potrzebujesz <strong data-bind='text: name'></strong>? <br />
Weź jeden za darmo <strong data-bind='text: price'></strong>.
</p>

Wewnątrz bloku with, Knockout.js używa PersonViewModel.featuredProduct jako zbindowany kontekst. Więc, text: name i text: price nie potrzebują referencji do obiektu rodzica. Dodajmy jeszcze nasz featureProduct

var featured = new Product("Fasolka senzu", 4.99);
this.featuredProduct = ko.observable(featured);

Podsumowanie

Zaprezentowałem możliwości bindowania z foreach, if, ifnot, with. Płynny przepływ danych daje kompletną kontrolę nad naszym ViewModel do wyświetlania widoku.

Bardzo ważne jest aby dokonać relacji między Knockout’owymi bindowaniami i właściwościami obserwowalnymi. W sumie technicznie oba są od siebie zależne. Teraz możemy sterować całą logiką która kryję się za widokiem, i możemy skupić się na wyglądzie indywidualnych elementów HTML.

Pozdro 🙂
 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*