W Angularze 2, podejście do formularzy to istna rewolucja. Kiedy pierwszy raz zajrzałem do dokumentacji, chwyciłem się za głowę. Mieszanka 40-stu różnych nazw, w tym interfejsy, dyrektywy, klasy. Można się zgubić. Najbliższą serię 3-4 artykułów, poświęcę wbudowanemu modułowi FormsModule oraz ReactiveFormsModule.
Na wstępie wypada wiedzieć, że Angular proponuje nam dwa różne podejścia do budowania i obsługi formularzy:
- template driven forms
- model driven forms (znane także jako reactive forms).
W pierwszym artykule z serii skupię się na pierwszym wariancie.
TEMPLATE DRIVEN FORMS
Aby rozpocząć pracę z formularzami, musimy zacząć od zaimportowania modułu FormsModule do modułu, w którym chcemy go użyć.
1 2 3 4 5 6 7 8 9 |
import { FormsModule } from '@angular/forms'; @NgModule({ imports: [ ... FormsModule ], ... }) |
W tym momencie uzyskujemy dostęp do API Forms w wybranym przez nas module. Zacznijmy od zbudowania najprostszego formularza z zastosowaniem Template Driven Forms:
1 2 3 4 5 6 7 8 9 |
<form #tdForm="ngForm" (ngSubmit)="submit(tdForm)"> <label>username:</label><input name="username" ngModel> <label>lastname:</label><input name="lastname" ngModel> <label>age:</label><input name="age" ngModel> <label>gender:</label><input name="gender" ngModel> <label>country:</label><input name="country" ngModel> <button type="submit">Send</button> </form> |
Powyższy kod jest zapewne dla Ciebie zrozumiały, więc skupmy się na konkretach. W pierwszej linijce widzimy:
1 |
<form #tdForm="ngForm" ...> |
Można powiedzieć, że w tym momencie odpalamy Template Driven Forms. Stworzyliśmy template variable o nazwie tdForm, do której przypisaliśmy dyrektywę ngForm, którą udostępnia nam FormsModule. Zaglądając do dokumentacji ngForm, widzimy pole:
Exported As: ngForm
Właśnie dlatego do #tdForm przypisałem wartość „ngForm”, bo pod tą nazwą jest wyeksportowana. Dzięki przypisaniu dyrektywy do zmiennej #tdForm, stworzyłem nową instancję tej dyrektywy.
NGFORM.VALUE – obiekt reprezentujący wartość formularza
Dyrektywa ngForm daje nam dostęp do wartości pobranych z inputów:
1 |
<form #tdForm="ngForm" (ngSubmit)="submit(tdForm)"> |
Implementacja metody submit:
1 2 3 |
submit(form) : void { console.log(form.value); } |
Value jest obiektem, reprezentującym wartości pobrane od użytkownika, jest to jedno z właściwości rozbudowanego obiektu ngForm, na który spojrzymy w całości w dalszej części artykułu. Pola obiektu value, powstają automatycznie na podstawie atrybutu „name” oraz dyrektywy ngModel, która nie potrzebuje mieć przypisanej wartości.
Zwrotka z konsoli po wypełnieniu danymi i kliknięciu buttona submit:
Bez żadnego kodu w komponencie, stworzyliśmy obiekt trzymający wartości pól formularza.
ONE-WAY ORAZ TWO-WAY-BINDING INPUTÓW
Jeśli chcemy nadać jakieś początkowe wartości do stanu formularza, możemy skorzystać z data-bound na dyrektywie ngModel:
1 |
<label>username:</label><input name="username" [ngModel]="username"> |
I w kodzie komponentu:
1 |
username : string = "Johnny" |
Nie stoi również nic na przeszkodzie, abyśmy podłączyli two-way-binding do naszego formularza:
1 2 |
<label>username:</label><input name="username" [(ngModel)]="username"> <pre>{{ username }}</pre> |
Plunker podsumowujący tą część artykułu:
W powyższym przykładzie, obiekt reprezentujący formularz był najprostszy z możliwych. Co jeśli chcemy mieć obiekt, który zawiera obiekty? Z pomocą przychodzi nam dyrektywa ngModelGroup.
NGMODELGROUP – tworzymy bardziej skomplikowany formularz
Szybko i prosto, możemy rozbudować nasz formularz dodając wyłącznie dykrektywę ngModelGroup, której przypiszemy nazwę grupy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<form #tdForm="ngForm" (ngSubmit)="submit(tdForm)"> <fieldset ngModelGroup="userData"> <label>username:</label><input name="username" ngModel> <label>lastname:</label><input name="lastname" ngModel> </fieldset> <fieldset ngModelGroup="otherUserData"> <label>age:</label><input name="age" ngModel> <label>gender:</label><input name="gender" ngModel> <label>country:</label><input name="country" ngModel> </fieldset> <button type="submit">Send</button> </form> |
Jak teraz wygląda obiekt reprezentujący nasz formularz:
Dzięki użyciu dyrektywy ngModelGroup (wymaga dyrektywy ngForm) utworzyliśmy sub-grupy. Link do dema po zastosowaniu ngModelGroup:
Spójrzmy jeszcze, jak wygląda cała instancja dyrektywy ngForm w naszym komponencie:
Nasze ngModelGroup są trzymane w obiekcie „controls„. Natomiast cały obiekt z wartościami z formularza, jak już wiemy, pod polem value. Widzimy również wiele pól pomocnych do walidacji formularza, takie jak touched, dirty, valid etc. W osobnym artykule omówię, jak wykorzystać te pola do walidacji.
RESETUJEMY STAN FORMULARZA
Dyrektywa ngForm udostępnia nam również metodę do resetowania stanu całego formularza, lub danej podgrupy (zadeklarowanej poprzez ngModelGroup).
Dodajemy button reset i jako parametr przekazujemy zmienną reprezentującą formularz:
1 |
<button (click)="reset(tdForm)">Reset</button> |
Kod metody reset():
1 2 3 |
reset(form) : void { form.reset(); } |
Lub jeszcze krócej, w samej templatce:
1 |
<button (click)="tdForm.reset()">Reset</button> |
Jeśli chcemy zresetować tylko pojedynczy ngModelGroup, przekazujemy nazwę groupy, która jest trzymana w controls:
1 2 3 |
reset(form) : void { form.controls['userData'].reset(); } |
Lub tworzymy template variable dla danego ngModelGroup:
1 2 3 4 |
<fieldset #userDataSubGroup="ngModelGroup" ngModelGroup="userData"> <label>username:</label><input name="username" ngModel> <label>lastname:</label><input name="lastname" ngModel> </fieldset> |
I wołamy reset:
1 |
<button (click)="userDataSubGroup.reset()">Reset userData subgroup</button> |
Live demo po dodaniu metod z resetami:
PODSUMOWANIE
Plusy:
- możliwość obsługi formularza praktycznie bez pisania custom kodu
- możemy dowolną ilość razy zagnieżdzać ngModelGroup w ngModelGroup
- wystarczające narzędzie do obsługi dużych formularzy
- przyjazna składnia z użyciem ngModel, zwłaszcza jeśli masz doświadczenie z budowaniem formularzy w Angular 1.x
Minusy:
- brak możliwości napisania unit testów (brak dostępu do DOM), musimy się zadowolić testami E2E
- ngModelGroup jest obiektem, nie ma możliwości aby zmienić jego typ na tablicę
Pingback: ANGULAR 2 MODEL DRIVEN FORMS, cz. I - Angular.love