Omówiłem już na wcześniejszej stronie jak tworzyć wiązania dwukierunkowe z kontrolkami HTML za pomocą dyrektywy ngModel. Na tej stronie opowiem co nieco o tym jak zrobić to samo za pomocą klasy FormControl i FormGroup. Przy wykorzystaniu tych klas nie działa bindowanie dwukierunkowe związane z dyrektywą ngModel a ustawianie i pobieranie danych z kontrolki wygląda inaczej. W tym przypadku również i podpinanie walidatorów będzie realizowane z poziomu obiektu klasy a ich tworzenie uprości się do stworzenia klasy z statyczną funkcją, która podpięta do obiektu klasy FormControl będzie walidowała daną kontrolkę.
Klasa FormControl i komunikacja z pojedynczą kontrolką
W celu zrealizowania komunikacji dwukierunkowej z kontrolką za pomocą obiektu klasy FormControl konieczne jest utworzenie takiego obiektu wewnątrz klasy komponentu i podpięcie jego do kontrolki w kodzie HTML co też i czynię z najdzikszą rozkoszą:
Tworzenie i walidacja formularzy z wykorzystaniem FormGroup
Klasa FormGroup umożliwia stworzenie obiektu grupy kontrolek formularza, do których dane zwrócone przez serwis mogą zostać w bardzo łatwy sposób wstawione jak i odczytane. Oto przykład, jak może wyglądać stworzenie grupy kontrolek i przypisanie im wartości zawartych w interfejsie:
W jednym formularzu można tak na prawdę zagnieździć więcej niż jeden obiekt zgrupowany w podobiekcie formularza. Oto przykład kodu HTML:
<form [formGroup]="formGroup">
<mat-form-field appearance="outline">
<mat-label>Kraj</mat-label>
<input matInput formControlName="yourCountry">
<mat-error>
<div *ngIf="formGroup.get('yourCountry').hasError('required')">To pole jest wymagane</div>
<div *ngIf="formGroup.get('yourCountry').hasError('minlength')">Długość ciągu znaków krótsza od 5</div>
<div *ngIf="formGroup.get('yourCountry').hasError('maxlength')">Długość ciągu znaków dłuższa od 20</div>
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Imię</mat-label>
<input matInput formControlName="firstName">
<mat-error>
<div *ngIf="formGroup.get('firstName').hasError('required')">To pole jest wymagane</div>
<div *ngIf="formGroup.get('firstName').hasError('minlength')">Długość ciągu znaków krótsza od 5</div>
<div *ngIf="formGroup.get('firstName').hasError('maxlength')">Długość ciągu znaków dłuższa od 20</div>
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Nazwisko</mat-label>
<input matInput formControlName="lastName">
<mat-error>
<div *ngIf="formGroup.get('lastName').hasError('required')">To pole jest wymagane</div>
<div *ngIf="formGroup.get('lastName').hasError('minlength')">Długość ciągu znaków krótsza od 5</div>
<div *ngIf="formGroup.get('lastName').hasError('maxlength')">Długość ciągu znaków dłuższa od 20</div>
</mat-error>
</mat-form-field>
<div formGroupName="address">
<mat-form-field appearance="outline">
<mat-label>Ulica</mat-label>
<input matInput formControlName="street">
<mat-error>
<div *ngIf="formGroup.controls['address'].get('street').hasError('required')">To pole jest wymagane</div>
<div *ngIf="formGroup.controls['address'].get('street').hasError('minlength')">Długość ciągu znaków krótsza od 5</div>
<div *ngIf="formGroup.controls['address'].get('street').hasError('maxlength')">Długość ciągu znaków dłuższa od 20</div>
</mat-error>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Numer domu</mat-label>
<input matInput type="number" formControlName="house">
<mat-error>
<div *ngIf="formGroup.controls['address'].get('house').hasError('required')">To pole jest wymagane</div>
<div *ngIf="formGroup.controls['address'].get('house').hasError('minlength')">Numer domu nie może mniejszy niż 1</div>
</mat-error>
</mat-form-field>
</div>
<button mat-button type="submit" [disabled]="formGroup.invalid">Wyślij</button>
</form>
A co jeśli chciałbym stworzyć formularz, który będzie umożliwiał dynamiczne dodawanie np. nowego rekordu danych? Czy da się coś takiego zrobić? Da się, albowiem FormBuilder ma opcję tworzenia tablicy przechowójący z kolei obiekty klasy AbstractControl. Tak się jakoś dziwnie składa, że po tej abstrakcyjnej klasie dziedziczy nie co innego ale klasa FormGroup. A oto i przebiegły sposób, w jaki można to wykorzystać do stworzenia prawdziwie rozszerzalnego formularza: