Wróć do strony głównej

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.

 

O autorze

Tomasz Nastały

JavaScript Developer, entuzjasta frameworka Angular oraz TypeScripta. Na co dzień lubię dzielić się wiedzą poprzez prowadzenie zajęć w jednym z trójmiejskich bootcampów i nagrywaniem kursów z Angulara.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

4 komentarzy

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *