Interaktywne bindowanie

Interakcja z użytkownikiem odbywa się przez elementy formularza na stronie internetowej. W Knockout.js, kiedy dojdziemy do możliwości edytowania pól przez użytkownika, Knockout.js zarządza aktualizacją w obu kierunkach. Inaczej mówiąc interaktywne bindownie działa w dwie strony. Mogą być one ustawione od strony programistycznej i aktualizowane w widoku oraz odpowiednio, mogą one być ustawione w widoku i czytane programowo.

Na przykład, można ustawić wartość w input form z ViewModel i będzie wynik wyświetlony na widoku. Ale, wpisywanie czegokolwiek przez użytkownika w pole, spowoduję aktualizację właściwości z ViewModel która jest połączona z widokiem. Reasumując głównym założeniem Knockouta.js jest upewnienie się, że zawsze występuję synchronizacja pomiędzy widokiem i ViewModel.

Knockout.js posiada 11 bindingów którę wchodzą w interakcję z użytkownikiem:

  • click: <method>- Wywołuje metodę z ViewModel kiedy element zostaję kliknięty.
  • value: <property>- Linkuję do wartości z elementu należącego do właściwości ViewModel.
  • event: <object>- Wywołuje metodę kiedy użytkownik inicjuje zdarzenie.
  • submit: <method>- Wywołuję metodę kiedy formularz jest zatwierdzony.
  • enable: <property>- Włącza element formularza który bazuję na pewnych warunkach.
  • disable: <property>- Wyłącza element formularza który bazuję na pewnych warunkach.
  • checked: <property>- Linkuję do radio button albo check box’a z ViewModel.
  • options: <array>- Definiuje element <select> z tablicy ViewModel.
  • selectedOptions: <array>- Definiuje aktywny element w polu <select>.
  • hasfocus: <property>- Definiuje czy element jest focused.

Tak samo, jak budowanie wyglądu za pomocą bindowania prezentowane w poprzednim wpisie, interaktywne bindowania są definiowane w atrybucie data-bind elementu HTML. Niektóre z nich takie jak click pracują z każdym elementem, ale inne takie jak checked mogą być używane tylko z konkretnymi elementami.

Forumularz HTML

Teraz utworzymy nowy plik HTML o nazwię interactive-bindings.html który będzie odpowiedzialny za rejestracje nowego klienta. Poniżej kod dla nowego pliku HTML:

<html>
<head>
  <meta charset='utf-8' />
<link rel='stylesheet' href='../style.css' />
</head>
<body>
  <form action="#" method="post">
    
  </form>

  <script type='text/javascript' src='knockout-2.1.0.js'></script>
  <script type='text/javascript'>
    function PersonViewModel() {
      var self = this;
      this.firstName = ko.observable("Przemek");
      this.lastName = ko.observable("Nyweron");
    }

    ko.applyBindings(new PersonViewModel());
  </script>
</body>
</html>

Binding click

Bindowanie click jest jednym z najprostrzych interakcji bindowania. Wywołuję metodę z ViewModel kiedy użytkownik kliknie w element. Dla przykładu, dodajmy teraz poniższy przycisk wewnątrz elementu <form>:

<p><button data-bind="click: saveUserData">Zapisz</button></p>

Kiedy użytkownik kliknie w przycisk, Knockout.js wywoła metodę saveUserData() z PersonViewModel.

this.saveUserData = function(model, event) {
  alert(model.firstName() + " sprawdzam!"
  if(event.ctrlKey) {
    alert("Trzymasz przyciśnięty przycisk.");
  }
}; 

W powyższym przykładzie, model odnosi się do najwyższego poziomu instancji ViewModel. Argument model będzie zawsze traktowany jako obecny element ViewModel, dzięki temu jest możliwy dostęp do indywidualnego elementu z listy w pętli foreach.

Bindowanie value

Bindowanie value jest bardzo podobne do bindowania text które było już prezentowane. Kluczową różnicą jest, że użytkownik może dokonać zmiany dzięki której ViewModel odpowiednio aktualizuję element. Dodamy teraz pola w których będzie można wprowadzić imię i nazwisko, poniższy kod wprowadzamy przed przyciskiem Zapisz:

<p>Imię: <input data-bind='value:firstName' /></p>
<p>Nazwisko: <input data-bind='value:lastName' /></p>

Bindując Value: firstName należy upewnić się, że element text w <input> jest zawsze tym samym co właściwość firstName w ViewModel, niezależnie od tego czy został zmieniony przez użytkownika czy przez aplikację. To samo dotyczy właściwości lastName.

Teraz dodamy przycisk który pozwoli wyświetlić dane użytkownika.

<p>
  <button data-bind='click: displayName'>
    Wyświetl imię 
  </button>
  <button data-bind='click: setName'>
    Ustaw imię
  </button>
</p>
this.displayName = function() {
  alert(this.firstName());
};
this.setName = function() {
  this.firstName("Tomek");
};

Kliknięcie w Wyświetl imię uruchomi odczyt z ViewModel właściwości firstName.
Kliknięcie w Ustaw imię ustawi wartość w ViewModel, powodując aktualizację elementu <input>.

Jeszcze raz, głównym założeniem pomiędzy podwójną synchronizacją jest skupienie się na danych. Po wszystkim ustawiamy bindowanie value, dzięki temu możemy zapomnieć o elementach HTML. Wystarczy pobrać lub ustawić właściwość związaną z ViewModel i Knockout.js zajmie się resztą.

Metody displayName i setName nie będą potrzebnę więc można je spokojnie usunąć.

Bindowanie event

Bindowanie event pozwala nasłuchiwać zdarzeń DOM lub jakichkolwiek elementów HTML. To jest coś jak generyczna wersja bindowania click. Ale, skoro zajmujemy się nasłuchiwaniem wielokrotnym dla zdarzeń, to wymaga od nas obiektu do mapowania dla zdarzeń do metod(podobne do bindowania attr). Na przykład, dodamy nasłuchiwanie dla mouseover i mouseout.

<p data-bind='event: {mouseover: showDetails, mouseout: hideDetails}'>
Imię: <input data-bind='value: firstName' />
</p>

Kiedy użytkownik uruchomi zdarzenie myszy, Knockout.js uruchomi metodę showDetails() dla naszego ViewModel. W innym wypadku, jeżeli kursor myszy opuści element, metoda hideDetails() uruchomi się. Oba przypadki mają taki sam parametr jak bindowanie click. Poniżej kod metod:

this.showDetails = function(target, event) {
  alert('Najechanie myszą na cel');
};
this.hideDetails = function(target, event) {
  alert('Zjechanie myszą z celu');
};

Teraz, gdy już mamy interakcję z polem imię, po najechaniu myszą powinniśmy zobaczyć wiadomość. Ale, zamist wyświetlania wiadomości, wyświetlmy informację dla każdego pola kiedy użytkownik najedzie na nie kursorem myszy. Do tego potrzebujemy inną zmienną obserable z PersonViewModel:

this.details = ko.observable(false);

Właściwość details działa jako przełącznik, którym możemy włączać lub wyłączać naszą metodę.

this.showDetails = function(target, event) {
  this.details(true);
};
this.hideDetails = function(target, event) {
  this.details(false);
};

Teraz możemy połączyć przełącznik z bindowaniem visible aby pokazać albo ukryć pole, w widoku dodajmy poniższy kod:

<p data-bind='event: {mouseover: showDetails, mouseout: hideDetails}'>
Imię: <input data-bind='value: firstName' />
<span data-bind='visible: details'>Twoje imię</span>
</p>

Element <span> powinien pojawiać się za każdym razem gdy użytkownik najedzie kursorem myszy na imię i zniknie kiedy kursor zniknie z pola.

Zdarzenia z parametrami niestandardowaymi

W Knockout.js można zaakceptować niestandardowy paranetr pochodzący z widoku do zdarzenia. Oznacza to, że mamy dostęp do informacji z widoku do ViewModel. W naszym przypadku, użyjemy niestandardowego parametru do identyfikowania które pole powinno zostać wyświetlone. Zamiast przełącznika, obserwowalny details będzie zawierać łańcuch znaków w wybranym elemencie. Najpierw, musimy dodać kilka zmian do naszego ViewModel:

this.details = ko.observable("");

this.showDetails = function(target, event, details) {
  this.details(details);
};
this.hideDetails = function(target, event) {
  this.details("");
};

To jedyna zmiana jaka należy nanieść.

Następnie, użyjemy funkcji do bindowalnego zdarzenia aby zaakceptować niestandardowy parametr do metody showDetails()

<p data-bind='event: {mouseover: function(data, event){showDetails(data,event, "firstName")}, mouseout: hideDetails} '>
</p>

Wewnętrzna funkcja dla mouseover jest opakowaniem dla naszego programu do obsługi showDetails(), zapewniając przy tym proste sposoby przetwarzania dodtakowych informacji. mouseout pozostaje niezmieniony. W końcu, potrzebujemy z aktualizować click: <span> kod poniżej:

<span data-bind='visible: details() == "firstName"'>Twoje imię</span>

Pole Imię powinno wyświetlać opis kiedy najedziemy na nie kursorem myszy i znikać kiedy kursor myszy zniknie z pola. Dzięki tym zmianiom możemy dodać więcej pól które będę obsługwiwane indywidualnie. Na przykkład włączymy szczegóły dla elementu input nazwisko:

<p data-bind='event: {mouseover: function(data, event){ showDetails(data,event, "lastName")}, mouseout: hideDetails}'>
Nazwisko: <input data-bind='value: lastName'/>
<span data-bind='visible:details() == "lastName"' Twoje nazwisko</span>
</p>

Bindowanie event może być troszkę skomplikowane do zrozumienia, ale gdy zrozumiesz jak to działa, wtedy zobaczysz nieograniczone możliwości projektowania reaktywnego.

Bindowanie enable/disable

Bindowanie enable i disable może być używane gdy chcemy włączyć bądź wyłączyć konkretne pole bazujące na określonych warunkach. Na przykład, chcemy dodać dwa pola dla numeru telefonu, nie ma sensu wymagać obu jednocześnie skoro nie mamy podanego głównego numeru. Można przechować właściwości jako zwykłe observable w PersonViewModel.Poniżej kod:

this.primaryPhone = ko.obserable("");
this.seconadryPhone = ko.obserable("");

Wartość primaryPhone która jest observable może linkować do pola z bindowaniem value:

<p>
Telefon główny: <input data-bind='value: primaryPhone' />
</p>

Jednakże, to nie ma większego sensu podawać drugiego telefonu bez ustalenia pierwszego jako głównego, więc aktywujemy drugi numer kiedy pierwszy nie będzie pusty:

<p>
Telefon drugi: <input data-bind='value: secondaryPhone, enable: primaryPhone' />
</p>

Teraz użytkownik będzie mógł podać drugi numer gdy pierwszy główny zostanie wypełniony. Bindowanie disable działa w ten sam sposób z tym, że jest zaprzeczeniem, ale w przeciwnym razie działa tak samo jak enable.

Bindowanie checked

Bindowanie checked jest wszechstronnie używanym elementem przedstawiającym różne zachowania w zależności od tego jak go użyjemy. Głównie, bindowanie checked jest używane do zaznaczania lub odznaczania elementów html typu check box i radio button.

Prosty checkbox

Zacznijmy z prostym check box’em:

<p>
Włącz mnie, bądź wyłącz gdy włączysz: <input data-bind='checked: annoyMe' type='checkbox' /></p> 
</p>

Powyższy kod doda check box do naszego pliku HTML i będzie linkować do właściwości annoyMe z ViewModel. Jak zawsze, jest to podwójne połączcenie. Kiedy użytkownik zaznaczy albo odznaczy checkbox, Knockout.js z akutalizuję ViewModel, i wtedy będzie mógł ustawić wartość dla właściwości ViewModel, i z aktualizuję do widoku. Nie zapomnij zaimplementować właściwości annoyMe obervable:

this.annoyMe = ko.obserale(true);

Używając bindowania checked tworzymy coś na wzór realcji jeden do jednego pomiędzy pojedynczym check box’em i zmienna typu Boolean jako obervable.

Tablica check-box

Można również użyć bindowania checked z tablicą. Kiedy bindujemy check box do tablicy observable, zaznaczone elementy odpowiadają do elementów w tablicy, poniższy obrazek pokazuję działanie:

Dla instancji, rozważmy poniższy kod:

this.annoyTimes = ko.observableArray(['ranek', 'wieczór']);

Możemy połączyć element pochodzący z tablicy observable do check box’a używając atrybutu value dla każdego elementu <input>.

<p>Wybierz: <input data-bind='checked:annoyMe' type='checkbox' /></p>
<div data-bind='visible: annoyeMe'>
  <div>
    <input data-bind='checked:annoyTimes' value='morning' 
                type='checkbox' />Rano
  </div>
  <div>
    <input data-bind='checked:annoyTimes' value='afternoon' 
                type='checkbox' />Popołudnie
  </div>
  <div>
    <input data-bind='checked:annoyTimes' value='evening' 
                type='checkbox' />Wieczór
  </div>
</div> 

Powyższy kod używa właściwości annoyMe która po kliknięciu pokazuję pozostałe trzy checkboxy.

Radio Buttons

Ostatnim bindowaniem dla checked jest radio buton. Zamiast typu Boolean albo tablicy. radio button łączy po przez atrybut value jako właściwość łańcucha znaków w ViewModel. Na przykład, możemy włączyć naszą tablicę check-box do grupy radio button, najpierw nanieśmy pewne zmiany:

this.annoyTimes = ko.observable('morning');

Teraz, jedyne co musimy zrobić to włączyć element <input> do radio buttons:

<input data-bind='checked:annoyTimes'
       value='morning' 
       type='radio'
       name='annoyGroup' />

Każdy <input> powinien mieć „radio” jako typ i nazwę jako annoyGroup.
Teraz, atrybuty value które są zaznaczone w swoich radio button będą zawsze przechowywane w właściwości annoyTimes

Bindowanie options

Bindowanie options jest definiowane w kontekście elementu <select>. Można użyć do listy typu drop-down albo do listy z możliwością wielokrotnego wyboru. Na początku, przyjrzymy się liście drop-down. Zedytujmy właściowść annoyTimes jeszcze raz:

this.annoyTimes = ko.observableArray([
  'Rano',
  'Popołudnie',
  'Wieczór'
]);

Teraz możemy zbindować powyższe do pola <select>:

<div data-bind='visible: annoyMe'>
  <select data-bind='options: annoyTimes'></select>
</div>

Teraz powinniśy mieć możliwość rozwinięcia listy wypełnioną radio button’ami, ale to nie jest zbyt fajne jeżeli na liście nie możemy ogarnąć który element jest zaznaczony. Dlatego, użyjemy bindowania value:

<select data-bind='options: annoyTimes, value: selectedTime'></select>

To pokazuję która właściwość z ViewModel przechowuję zaznaczone łańcuchy znaków. Wciąć musimy zdefiniować tą właściwość:

this.selectedTime = ko.obserable('Popołudniu');

Ponownie, jest to relacja która idzie w obu kierunkach. Ustawienie wartości z selectedTime spowoduję zmiane w zaznaczonym elemencie z listy drop-down i na odwrót.

Użycie Obiektu jako Options

Kombinacja options i bindowanie value daje nam wszystkie potrzebne narzędzie do pracy z listą drop-down przechowującą łańcuch znaków. Jednakże, jest to bardziej wygodne gdy możemy zaznaczyć cały obiekt JavaScript i użyć go jako listy drop-down. Na przykład, poniższa lista definiuję produkty:

this.products = ko.observableArray([
  {name: 'Piwo1', price: 11},
  {name: 'Piwo2', price: 12},
  {name: 'Piwo3', price: 13}
)];

Kiedy spróbujemy stworzyć element <select>, wszystkie nasze obiekty będą renderowane jako [object object].
Na szczęście, Knockout.js pozwala na parametr optionsText który definiuje właściwość obiekut do wyrenderowania w elemencie <select>.

<select data-bind='options: products, optionsText: "name", value: favoriteProduct'></select>

Dla tego przykładu trzeba zdefiniować favoriteProduct observable w ViewModel. Knockout.js zawsze zastosuję tą właściwość z obiektem z PersonViewModel.products nie jako łańcuch znaków.

Bindowanie selectdOptions

Inne możliwości wyrenderowania elementu HTML’owego <select> jest lista wielokrotnego wyboru. Konfiguracja listy wielokrotnej jest mocno zbliżona do listy drop-down, z wyjątkiem tego, że w drop-down można zaznaczyć tylko jeden element. Więc, zamist używania bindowania valuedo przechowywania zaznaczeń, użyjemy bindowania selectedOptions:

<select data-bind='options: products, optionsText: "name", selectedOptions: favoriteProduct' size='3' multiple='true'></select>

Atrybut size definiuję liczbę widocznych opcji, i atrybut multiple=’true’ przekształca listę w listę wielokrotnego użytku. Zamiast właściowści łańcucha znaków, favoriteProducts powinno być tablicą:

var brats = {name: 'Brats', price: 7.99};
this.products = ko.observableArray([
  {name: 'Piwo', price: 10},
  brats,
  {name: 'Piwo2', price: 12}
]);
this.favoriteProducts = ko.observableArray([brats]);

Należy pamiętać, że musimy dostarczyć referencje obiektu (brats) do obu obervableArray products i favoriteProducts dla Knockout.js tak wygląda prawidłowa inicjalizacja elementu <select>.

Bindowanie hasfocus

Trafna nazwa dla bindowania hasfocus pozwala manualnie ustawić focus na interaktywnym elemencie używanym przez właściwość ViewModel. Jeżeli z jakiś dziwnych przyczyn, chcielibyśmy pole „Telefon główny” zainicjować focus, to musimy dodać bindowanie hasfocus:

<p>
Telefon główny: <input data-bind='value: primaryPhone, hasfocus: phoneHasFocus' />
</p>

Teraz możemy dodać typ Boolean observable aby poinformować Knockout.js aby dodał focus:

this.phoneHasFocus = ko.observable(true);

Ustawiając tą właściwość gdziekolwiek w aplikacji, możemy precyzyjnie kontrolować przepływ focus z elementami HTML. W dodatku możemy użyć hasfocus do śledzenia ruchów użytkownika wykonywanych na polach wielokrotnego użytku.

 
Pozdro 🙂
 

Dodaj komentarz

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

*