Scopul acestei discuții a fost de a arăta potențialul Formelor Reactive, în special modul în care acestea gestionează forme complexe cu multă validare.

Fiecare control din formular (select, input, checkbox, etc...) are o proprietate valueChanges, un Observabil la care ne putem abona pentru a observa schimbările în timp și a transforma valorile cu operatorii RxJS într-un mod funcțional. cale. Prin urmare, un formular este tratat ca un flux de date în care fiecare câmp acționează ca un Observabil.

Să ne uităm la un exemplu de formă reactivă:

import { Component, OnInit } from \'@angular/core\';
import { FormControl } from \'@angular/forms\';

@Component({
    selector: \'app-login-form\',
    template: `
      <input type="text" [formControl]="name">
    `
)
export class LoginComponent implements OnInit {
  name = new FormControl(\'name\');
  ngOnInit() {
     this.name.valueChanges.subscribe(data => console.log(data));
  }
}

După cum puteți vedea, avem proprietatea declarată ca FormControl și o putem inițializa cu o valoare arbitrară și o putem lega la formularul de intrare din șablon. În acest fel, ele sunt întotdeauna sincronizate. Pot observa modificările de valoare făcute în șablon cu un abonament explicit la proprietatea valueChange sau pot folosi metoda setValue pentru a modifica valoarea din componentă și a o reflecta în șablon . Acționează ca o legare de date bidirecțională.

Într-un formular bazat pe șabloane, fiecare element de formular este conectat la proprietatea Model (în interiorul logicii componentei) cu o directivă ngModel. Acesta este motivul pentru care formularele TPL sunt asincrone. Directiva ngModel este conectată la „ciclul de viață al aplicației de detectare a modificării”, astfel încât valoarea dintre elementul HTML și componentă este actualizată după o „bifare” a buclei de evenimente.

Datorită bibliotecii RxJS, putem folosi metoda fromEvent pentru a observa și a ne abona la un eveniment. Folosind decoratorul Angular ViewChild și fără a folosi Reactive Forms, putem lua o referință a unui element din DOM (cum ar fi un getElementById) și putem profita de operatorii RxJS pentru a manipula datele din interiorul unui element de intrare:

Desigur, trebuie să cunoașteți puțin RxJS, cel puțin operatorii de bază precum map, filter, take, debounceTime și distinctUntilChanged.

Până acum, nu am folosit constructorul de bază al Formularelor reactive din FormBuilder, cum ar fi FormControl, FormGroup și FormArray. Le putem folosi cu FormBuilder sau le putem folosi singuri ca în exemplul anterior.

Următorul exemplu este puțin mai greu, deoarece introducem FormGroup și FormBuilder pentru a construi formularul și pentru a avea o scalabilitate mai bună a formularelor:

import { Component, OnInit } from \'@angular/core\';
import { FormControl, FormBuilder, FormGroup } from \'@angular/forms\';
 
@Component({
    selector: \'app-login-form\',
    template: `
    <form [formGroup]="loginForm" (ngSubmit)="submit()">
      <input type="text" [formControl]="username">
 
      <button [disabled]="loginForm.invalid">Login</button>
    </form>
  `
})
export class LoginComponent implements OnInit {
  loginForm: FormGroup;
  username = new FormControl(\'username\');
  constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
    this.loginForm = this.formBuilder.group({
      username: this.username
    });
    this.loginForm.valueChanges.subscribe(data => console.log(data));
  }
 
  submit() {
    console.log(this.loginForm.value);
  }
}

Angular oferă un serviciu numit FormBuilder care vă permite să scrieți mai puțin cod. Pentru a-l folosi, trebuie să îl injectăm în interiorul unei componente prin „Injecție de dependență unghiulară”. Acum putem folosi FormGroup pentru a ne construi cu ușurință formularele. În interiorul FormGroup avem un obiect de perechi cheie-valoare, unde cheia este numele controlului, iar valoarea este o serie de opțiuni, cum ar fi valoarea inițială, validatorii încorporați și unele validatoare personalizate.

Validator personalizat

Un validator personalizat este o funcție care primește ca intrare controlul unde este aplicat și, în interiorul corpului, putem gestiona un cod de verificare, cum ar fi o potrivire cu o expresie regulată.

În logica componentelor avem trei moduri de a obține o referință de control:

  1. form.get(‘companie’)
  2. form.controls[‘companie’]
  3. formează.controlează.companie

unde „form” este referința formei mele. Toate sunt echivalente. Acest lucru poate fi util pentru a controla elementul nostru HTML în logica componentei.

Formă imbricată

Un Grup de formulare imbricate este un grup de elemente de intrare pe care trebuie să le validăm împreună. Ne permite să creăm o serie de obiecte:

Câteva exemple de lucru:

import { Component, OnInit } from \'@angular/core\';
import { FormBuilder, FormGroup, Validators } from \'@angular/forms\';
 
@Component({
    selector: \'app-login-form\',
    template: `
    <form [formGroup]="loginForm" (ngSubmit)="submit()">
      <input type="text" formControlName="username">

      <input type="password" formControlName="password">
      <button [disabled]="loginForm.invalid">Login</button>
    </form>
  `
})
export class LoginComponent implements OnInit {
  loginForm: FormGroup;
  constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
    this.loginForm = this.formBuilder.group({
      username: [\'\', [Validators.required, Validators.minLength(2)]],
      password: [\'\', [Validators.required, Validators.minLength(4)]],
    });
    this.loginForm.valueChanges.subscribe(data => console.log(data));
  }
 
  submit() {
    console.log(this.loginForm.value);
  }
}
import { Component, OnInit } from \'@angular/core\';
import { FormControl, FormBuilder, FormGroup, Validators } from \'@angular/forms\';
 
@Component({
    selector: \'app-login-form\',
    template: `
    <form [formGroup]="form" (ngSubmit)="send()">
      <input type="text" formControlName="username">
      <div formGroupName="carInfo">
       
        <input type="text" formControlName="model">
        <input type="number" formControlName="kw">
      </div>
      <i class="fa fa-check" *ngIf="form.get(\'carInfo\').valid && form.valid"></i>
      <button [disabled]="form.invalid">Login</button>
    </form>
  `
})
export class LoginComponent implements OnInit {
  form: FormGroup;
 
  constructor(private formBuilder: FormBuilder) {}
  ngOnInit() {
    this.form = this.formBuilder.group({
      username: [\'\', Validators.required],
      carInfo: this.formBuilder.group({
        model: [\'\', Validators.required],
        kw: [\'\', Validators.required],
      })
    });
    this.form.valueChanges.subscribe(data => console.log(data));
  }
 
  send() {
    console.log(this.form.value);
  }
}

De asemenea, putem valida un câmp pe baza valorii unui alt element de intrare, cum ar fi câmpurile de formular pentru potrivirea parolei.

Fiecare grup de formulare poate avea un al doilea parametru în care putem specifica un validator personalizat pentru grupul de formulare în sine. Acest validator este o funcție în care putem trece referința celor două câmpuri de parolă și facem o verificare a potrivirii în interiorul acestuia.

Când formularele noastre cresc în complexitate, le putem împărți în FormGroup sau ne putem împărți UI în Componente. Deci, fiecare FormGroup poate fi reprezentat cu un element personalizat:

Forme dinamice

Cu FormArray putem afișa elementul în mod dinamic, este o variantă a FormGroup în care datele sunt serializate în interiorul unei matrice în loc de un obiect ca în FormGroup. Acest lucru este util atunci când nu știți câte controale vor fi găzduite în formularele dvs. dinamice.

În cele din urmă, putem genera un formular în timpul execuției bazat pe o structură JSON care poate proveni dintr-un apel XHR REST. Structura JSON poate conține proprietăți precum eticheta, tipul (de intrare), validatori etc.

În interiorul șablonului, vom folosi directiva *ngFor și ngSwitch pentru a selecta ce tip de intrare vom reda șablonului.

Problema cu această soluție este că ngSwitch poate crește, așa că putem defini o altă soluție folosind o Hash Map pentru componentele noastre pe care o putem folosi pentru a crea șablon de formulare. Pentru fiecare control putem crea o componentă. Deci, în codul JavaScript avem un ciclu pentru citirea configurației JSON și construirea unui control de formular în timpul rulării, în timp ce pentru șablon putem folosi un *ngFor, iar pentru fiecare iterație putem folosi o directivă numită dynamicField care instanțează componenta potrivită pentru noi, pe baza configurației controlului.

După cum putem vedea, acest subiect este imens și nu putem acoperi fiecare aspect într-o singură lucrare ca aceasta, dar sper că v-am trezit curiozitatea.