ANGULAR 2 – Custom Form Controls

Przed przeczytaniem artykułu, polecam zapoznać się podstawami Reactive Forms z poprzednich artykułów.

W tym artykule przedstawię, jak tworzyć własne kontrolki (form Control). Oprócz kontrolek, które Angular obsługuje od ręki, tzn:

  • selecty, inputy, textarea, radio buttony, checkboxy

posiadamy również możliwość stworzenia własnych, unikalnych kontrolek. Obecnie istnieje ich sporo na rynku, dużą popularnością cieszą się date-pickery i multiselecty. Pokażę jak stworzyć własny Custom Form Control – przygotujemy rozbudowany dropdown.

Poniżej link do przykładu, który omówię:

LIVE EXAMPLE

Przygotowujemy komponent

Standardowy select składa się z opcji, które zawierają tylko nazwy i posiada pojedynczą wartość dla danej opcji. Natomiast przykład, który rozpatrzymy, dotyczy dropdowna, który zawiera wiele informacji, a wartością dla danej opcji, jest pełny obiekt. Wygląda to następująco (wybaczcie design ; )):

Skoro już wiemy co chcemy osiągnąć, to czas rozpocząć pracę od przygotowania templatki i klasy komponentu.

Klasa:

Bez magii:

  • Input() dla tablicy produktów
  • boolean isListVisible, który posłuży nam do pokazania listy po kliknięciu buttona
  • i metoda selectProduct(), która przypisze wybrany produkt do pola value

Templatka:

Powyższy kod również powinien być dla Ciebie jasny. Puściliśmy *ngFor po tablicy produktów, aby wygenerować je w liście, oraz na każdy element listy przypisaliśmy na clicka metodę, która wybiera dany produkt.

Na ten moment mamy prosty, głupiutki komponent. Próba użycia komponentu jako FormControla w postaci:

zakończy się niepowodzeniem. No to czas nauczyć nasz komponent rozmawiać z formularzami.

ControlValueAccessor

ControlValueAccessor możesz traktować jako klej pomiędzy elementem DOM (nasz komponent) a modelem reprezentującym formularz (np. modelem stworzonym przez FormBuildera). Po bardziej „programistycznemu” jest to interfejs:

ControlValueAccessor – link do dokumentacji

Jeśli spojrzysz do API Angulara, to zobaczysz, że istnieje parę typów ValueAccesor:

  • DefaultValueAccessor – dla inputa i textarea
  • CheckboxControlValueAccessor / RadioControlValueAccessor– dla checkboxa i radio buttona
  • SelectControlValueAccessor / SelectMultipleControlValueAccessor– dla selectów

Zatem każdy ValueAccessor mówi danemu elementowi, jak zapisywać wartość i jak nasłuchiwać na zmiany.

——

Do naszego custom controla wykorzystamy ControlValueAccessor. Posiada on następujące metody:

  • writeValue(obj : any) : void – zapisuje nową wartość z modelu formularza do widoku
  • registerOnChange(fn: any) : void  rejestruje listenera na Change Events, metoda używana wewnętrznie przez Angulara. Nie powinniśmy jej usuwać, bez tej metody próba użycia control.valueChanges.subscribe(…) w Reactive Forms, w celu nasłuchiwania na zmiany wartości, zakończony się niepowodzeniem
  • registerOnTouched(fn: any) : void – działa na podobnej zasadzie co registerOnChange, z tą różnicą, że na słuchuje na Touch Events.
  • setDisabledState(isDisabled: boolean) : void – funkcja jest wywołana, jeśli kontrolka zmienia stan z enabled na disabled lub odwrotnie (control.enable() i control.disable()). Możemy przechwycić wartość isDisabled z parametru i użyć jej na dyrektywie [disabled] w celu zablokowania elementu w DOM, który może posiadać taki atrybut.

Skoro już znamy szczegóły, czas dodać nasz interfejs.

Implementujemy interfejs ControlValueAccessor

Wracamy do klasy:

Co się wydarzyło:

  • do dekoratora @Component dodaliśmy nowy provider, pod tokenem NG_VALUE_ACCESSOR, rejestrujemy nasz nowy ValueAccessor, którym jest nasz komponent. Czym jest dokładnie useExisiting oraz  forwardRef tłumaczyłem w artykule o własnych walidatorach: Angular : Custom validators
  • zaimplementowaliśmy interfejs ControlValueAccessor
  • dodaliśmy metody z interfejsu
  • pod polem value, trzymana jest aktualna wartość kontrolki (w naszym przypadku będzie to obiekt, reprezentujący produkt z listy)
  • dodaliśmy metodę getCssForSelectedProduct(product), która doda nam odpowiednią klasę CSS dla wybranego elementu z listy

W tej chwili nasz Custom Form Control potrafi już się porozumieć z formularzami. Zwróć uwagę, że nigdzie nie wywołujemy metod z interfejsu. Metoda writeValue(value) sama wie, że jej parametr value to jest wartość przekazana z modelu formularza (stworzonego np. przez FormBuildera). Teraz możemy już użyć naszego selecta w obu typach formularzy:

Link do omówionego przykładu:
LIVE EXAMPLE

Podsumowanie

Generalnie genialna sprawa. Możemy stworzyć dowolony komponent, który będzie potrafił przekazać swoją wartość do modelu reprezentującego formularz.Wartością może być cokolwiek, np. skomplikowany obiekt. Jeśli chcesz sprawdzić jak wygląda mocno rozbudowany custom Form, polecam spojrzeć do kodu datePickera:

http://kekeh.github.io/mydatepicker/

https://github.com/kekeh/mydatepicker/blob/master/src/my-date-picker/my-date-picker.component.ts

 

Dodaj komentarz

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