11 lut 2017
5 min

ANGULAR 2 MODEL DRIVEN FORMS, cz. III

MODEL DRIVEN FORMS – dynamiczne formularze

W poprzednich artykułach (cz I i cz II) operowałem na formularzach, które składy się ze stałej liczby grup (FormGroup) i kontrolek (FormControl). W tym artykule pokażę, jak dodawać / usuwać / iterować po grupach w formularzu, czyli po prostu tworzyć dynamiczne formularze. Pomoże nam w tym klasa FormArray.

Przykład który rozpatrzymy, jest popularny w kreatorach CV. Użytkownik dodaje kolejne doświadczenie, które z reguły składa się ze stanowiska, zakresu dat, nazwy firmy i miasta. Oczywiście ma również możliwość usunięcia danego doświadczenia. W naszym przypadku, będzie to wyglądać tak:

Każde kliknięcie „Add position”, doda nam kolejną pozycję z doświadczeniem.

Przed przerobieniem tego artykułu, polecam zapoznać się z dwiema poprzednimi częściami, inaczej część treści będzie dla Ciebie niezrozumiała.

Link do plunkera:

LIVE EXAMPLE

ARRAY FORM

Jak wspomniałem w poprzednich artach, pojedynczy FormGroup składa się z dowolnej ilości FormControl, oraz może posiadać zagnieżdżone FormGroup. Natomiast FormArray, może wyglądać następująco:

Model Driven Forms - FormArray

Każdy FormControl lub FormGroup znajduje się pod kolejnym indeksem, tak jak to jest w standardowych tablicach. Co ważne, grupy i kontrolki mają swoje nazwy, natomiast w FormArray do konkretnej grupy dostajemy się wyłącznie po indeksie.

FORM ARRAY W KLASIE KOMPONENTU

FormArray możemy stworzyć na dwa sposoby:

1) Standardowo poprzez new FormArray()

this.formArray = new FormArray([...]);

2) Używając klasy FormBuilder

this.myArray = this.formBuilder.array([...]);

W przypadku naszego kodu, skorzystam z FormBuildera:

this.modelForm = this.formBuilder.group({
 firstname: ['', Validators.required],
 lastname: ['',[Validators.required, Validators.minLength(3)]],
 positions: this.formBuilder.array([])
});

Teraz przygotujemy metodę, która będzie nam zwracać pożądany FormGroup:

buildPosition() : FormGroup {
 return this.formBuilder.group({
   job: '',
   company: '',
   city: ''
 });
}

Przygotujemy również gettera, którego wykorzystamy na widoku oraz w klasie. Zwróć uwagę, że dodaję cast operator <FormArray>, bez tego zwrócony typ to AbstractControl.

get positions() : FormArray {
 return <FormArray>this.modelForm.get('positions');
}

Oraz metodę do pushowania nowych grup:

addPosition() : void {
 this.positions.push(this.buildPosition());
}

Powyższy .push, nie jest „pushem” wbudowanym w JS. Jest to metoda klasy FormArray, i wygląda następująco:

push(control : AbstractControl) : void

Z racji, że ustawiliśmy gettera (get) na metodzie positions(), metodę PUSH wywołujemy bezpośrednio na this.positions.

Potrzebujemy jeszcze metody do usuwania grup:

removePosition(i) : void {
 this.positions.removeAt(i);
}

RemoveAt(i), jest również metodą udostępnioną przez klasę FormArray i pozwala nam usunąć grupę na przekazanym indeksie.

removeAt(i : number) : void

Podsumujmy, co mamy:

  • strukturę formularzu zbudowaną poprzez formBuildera
  • metodę, która zwraca nową grupę z pozycją zawodową
  • metodę, która pushuje do Positions: FormArray([…]) nową grupę
  • oraz kolejną metodę, która pozwoli nam usunąć grupę na dowolnym indeksie

Wszystko co potrzebujemy po stronie kodu – mamy.

FORM ARRAY W WIDOKU KOMPONENTU

Czas na HTML (nie jest to HTML całego formularza):

<button (click)="addPosition()">Add position</button>
<div formArrayName="positions" *ngFor="let pos of positions.controls; let i = index">

  <h3>Position {{ i + 1}}</h3>
  <button (click)="removePosition(i)">remove (X)</button>

  <fieldset [formGroupName]="i">
    <label attr.for="{{ 'posId' + i }}">Job:</label>
    <input id="{{ 'posId' + i }}" formControlName="job">
 
    <label>Company:</label>
    <input name="company" formControlName="company">
 
    <label>City:</label>
    <input name="city" formControlName="city">
  </fieldset>

</div>

Co wykorzystaliśmy na widoku?

  • dyrektywa formArrayName – tutaj przekazujemy nazwę zadeklarowaną w formBuilderze, gdzie tworzymy główną strukturę formularza
  • dodanie dyrektywy *ngFor, która iteruje po positions.controls – pole controls zawiera tablicę z FormGroupami/FormControlsami, przypisaliśmy również index iteracji do zmiennej i
  • dyrektywa [formGroupName], do której przekazujemy jako parametr aktualny index. Zwróć uwagę, że korzystamy z data-bound, czyli formGroupName jest kwadratowych nawiasach
  • formControlName, które jest Ci znane z poprzednich artów
  • metoda removePosition(i) przyjmująca jako parametr index grupy
  • metoda addPosition(), która dodaje nową grupę na widoku

Poniżej link do podsumowującego Plunkera:

LIVE EXAMPLE

Co jeszcze min. udostępnia nam klasa formArray:

  • length – odczytanie długości tablicy
  • setControl – zamiana dowolnej grupy / kontrolki na inną
  • insert – dodanie grupy / kontrolki na dowolnym indeksie
  • setValue – nadpisanie całej zawartości tablicy
  • patchValue – nadpisanie dowolnego elementu w tablicy

Dokładny opis metod i przyjmowanych parametrów, znajduje się w dokumentacji klasy:

FORM ARRAY CLASS

Jeszcze trik na koniec: jeśli na starcie chcesz posiadać jedną grupę w FormArray, możesz to zrobić w następujący sposób:

positions: this.formBuilder.array([ this.buildPosition() ])

PODSUMOWANIE

Krótko i na temat Model Driven Forms: bardzo proste i skuteczne narzędzie, z pewnością się przyda w wielu sytuacjach. Możesz również pomyśleć o walidacji – np. zablokować dodanie kolejnej grupy, póki poprzednia nie została odpowiednio wypełniona.

Sprawdź też:

ANGULAR 2 MODEL DRIVEN FORMS część I

oraz ANGULAR 2 MODEL DRIVEN FORMS część II

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.