Angular – Asynchroniczne walidatory

Dzisiaj wpis na temat asynchronicznej walidacji formularzy. O ile implementacja asynchronicznej walidacji w Angularze jest bardzo prosta, to już poprawne zakodowanie takiego walidatora wymaga pewnej wiedzy z RxJS.

Zanim przejdziemy do kodowania, zastanówmy się, kiedy może się przydać taki typ walidacji?

Na pewno w każdej sytuacji, kiedy chcemy mieć natychmiastowo feedback z serwera, czy dana wartość wpisana do formularza, istnieje już w bazie danych. Dzięki temu użytkownik może od razu poprawić wartość, zamiast czekać np. na informację po kliknięciu przycisku submit.

Rozważymy przykład, w którym użytkownik tworzy projekt i musi mu nadać unikalny klucz. W sytuacji, kiedy klucz jest już zajęty w bazie, chcemy od razu użytkownikowi wyświetlić informację o błędzie.

Asynchroniczny walidator – nieco teorii

Asynchroniczny walidator jest funkcją, która:

  • przyjmuje jako parametr instancję FormControl lub FormGroup
  • zwraca obiekt Observable
  • może przyjmować dodatkowe parametry (np. usługę), w takiej sytuacji tworzymy funkcję, która zwraca funkcję, parametr z kontrolką umieszczamy w funkcji zagnieżdżonej, a parametr z usługą w funkcji nadrzędnej
  • jeśli asynchroniczny walidator zwróci NULL, oznacza, że błąd nie wystąpił i wszystko jest OK, czyli identycznie jak w synchronicznych walidatorach

Oraz trzeba pamiętać, że:

  • Angular najpierw wykona synchroniczną walidację
  • w przypadku, jeśli synchroniczna walidacja zwróciła nam błędy, to asynchroniczne walidatory się NIE uruchomią

Implementacja walidatora

Poznaliśmy niezbędną teorię, więc czas zabrać się za mięsko 😉 Zbierzmy najpierw wymagania dla naszego walidatora. Czego oczekujemy?

  • w przypadku jak użytkownik zacznie wpisywać literki, chcemy opóźniać request do servera o 500ms, aby nie robić requesta co każdy znak (użyjemy timer z RxJS), co mogłoby być niezbyt dobre, zwłaszcza jak użytkownik bardzo szybko wpisuje znaki
  • w sytuacji, gdy poprzedni request się jeszcze nie skończył i został już wysłany nowy, to chcemy poprzedni anulować (użyjemy switchMap z RxJS)
  • walidator ma przyjmować usługę (serwis) jako parametr

Znamy już wymagania, przygotujmy jeszcze elementy, dzięki którym przetestujemy nasz walidator na StackBlitz.

Tworzę prosty serwis, udający zwrotkę z serwera, czy dany klucz jest już zajęty:

Naszym zajętym kluczem w bazie danych jest wartość „angularlove123”. Jeśli użytkownik spróbuje wpisać taki klucz do formularza, ma natychmiastowo otrzymać błąd. Opóźniamy Observable o 300ms, udając, że idzie odpowiedź z serwera. Oczywiście w tym miejscu, powinien iść prawdziwy strzał do API, który np. zwróci nam obiekt z wartością exists: true lub false.

Skoro mamy już serwis, czas zabrać się za walidator:

Po kolei:

  • używamy timer(500), aby opóźniać kontakt z serwerem i nie strzelać do API co każdą wpisaną literkę
  • następnie w switchMap sięgamy po metodę checkKey usługi ProjectService, dzięki switchMap poprzednie requesty są cancelowane, kiedy do strumienia wpadła już nowa wartość
  • do checkKey przekazujemy control.value, czyli wartość, którą wpisuje użytkownik do inputa
  • mapujemy odpowiedź, zwracamy obiekt z błędem jeśli klucz jest zajęty, a jeśli nie to zwracamy NULL

Podłączenie walidatora do kontrolki

Pozostaje nam teraz dodać walidator do kontrolki:

Templatka:


Powyżej, mamy dostęp do errora pod kluczem w jakim go dodaliśmy, czyli keyExists. Korzystamy z metody hasError aby sprawdzić, czy wystąpił.

Klasa komponentu:

Przekujemy do kontrolki nasz walidator  pod kluczem asyncValidators (tablica asynchronicznych walidatorów). Można również go dodać jako trzeci parametr w new FormControl, od razu w postaci tablicy:

Jednak zapis przy użyciu tablic jest mniej elegancki, w naszym przypadku wymusza na nas dodanie pustej tablicy synchronicznych walidatorów i jest mniej czytelny. Sugeruję dodawać walidatory pod kluczami validators i asyncValidators.

Link do przykładu na StackBlitz:

Example demo

Podsumowanie

Jak zauważyłeś, przy użyciu RxJS, możemy w bardzo łatwy sposób implementować wydajne, asynchroniczne walidatory. Framework Angular w przyjazny sposób umożliwia dodawanie ich do kontrolek lub całych grup kontrolek. Dodatkowo dzięki asynchronicznym walidatorom, nasza aplikacja staje się bardziej user friendly.

Dodaj komentarz

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