Introducerea formelor în unghiular 4 Forme reactive

Ce veți crea

Aceasta este a doua parte a seriei despre introducerea formelor în unghiular 4. În prima parte, am creat un formular folosind abordarea bazată pe șablon. Am folosit directive cum ar fi ngModel, ngModelGroup și ngForm pentru supraîncărcarea elementelor de formă. În acest tutorial, vom lua o abordare diferită în ceea ce privește construirea formelor - modul reactiv. 

Formulare reactive

Formele reactive adoptă o abordare diferită față de cea a formularelor bazate pe șablon. Aici, noi creăm și inițializăm forme de control obiecte în clasa noastră de componente. Ele sunt obiecte intermediare care dețin starea formei. Atunci le vom lega la formează elemente de control în șablon.

Obiectul de control al formei ascultă orice modificare a valorilor de control de intrare și acestea se reflectă imediat în starea obiectului. Deoarece componenta are acces direct la structura modelului de date, toate modificările pot fi sincronizate între modelul de date, obiectul de control al formularului și valorile de control de intrare. 

Practic, dacă construim un formular pentru actualizarea profilului de utilizator, modelul de date este obiectul utilizator preluat de pe server. Prin convenție, aceasta este adesea stocată în proprietatea utilizatorului (this.user). Obiectul de control al formularului sau modelul de formular va fi legat de elementele de control ale formei reale ale șablonului.

Ambele modele ar trebui să aibă structuri similare, chiar dacă nu sunt identice. Cu toate acestea, valorile de intrare nu ar trebui să curgă direct în modelul de date. Imaginea descrie modul în care intrarea utilizatorului din șablon se îndreaptă spre modelul de formular.

Să începem.

Cerințe preliminare

Nu trebuie să fi urmat prima parte a acestei serii, pentru ca partea a doua să aibă sens. Cu toate acestea, dacă sunteți nou în formularele Angular, aș recomanda foarte mult să treceți prin strategia bazată pe șablon. Codul pentru acest proiect este disponibil în depozitul meu GitHub. Asigurați-vă că sunteți pe ramura dreaptă și apoi descărcați zipul sau, alternativ, clonați repo-ul pentru a vedea forma în acțiune. 

Dacă preferați să începeți de la zero, asigurați-vă că aveți CLI Angular instalat. Folosește ng comandă pentru a genera un nou proiect. 

$ ng new SignupFormProject

Apoi, generați o nouă componentă pentru SignupForm sau creați unul manual. 

ng genera componenta SignupForm

Înlocuiți conținutul app.component.html cu asta:

 

Aici este structura directorului pentru src / director. Am eliminat câteva fișiere ne-esențiale pentru a păstra lucrurile simple.

. ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── înscriere formă │ │ ├ ── de înscriere form.component.css │ │ ├── înscriere form.component.html │ │ └── înscriere form.component.ts │ └── User.ts ├── index.html ├── principal .ts ├──┘ polyfills.ts ├──────────────────────────────────────────────────────────────────────────── 

După cum puteți vedea, un director pentru SignupForm componentă a fost creată automat. Acolo este cea mai mare parte a codului nostru. De asemenea, am creat un nou User.ts pentru stocarea modelului nostru de utilizator.

Șablonul HTML

Înainte de a ne scufunda în șablonul de componente actual, trebuie să avem o idee abstractă despre ceea ce construim. Deci, aici este structura de formă pe care o am în mintea mea. Formularul de înscriere va avea mai multe câmpuri de introducere, un element selectat și un element de casetă de selectare. 


Aici este șablonul HTML pe care îl vom folosi pentru pagina noastră de înregistrare. 

Șablon HTML

 
Inscrie-te

Clasele CSS folosite în șablonul HTML fac parte din biblioteca Bootstrap utilizată pentru a face lucrurile frumoase. Deoarece acesta nu este un tutorial de design, nu voi vorbi prea mult despre aspectele CSS ale formularului decât dacă este necesar. 

Configurarea formularului de bază

Pentru a crea un formular reactiv, trebuie să importați ReactiveFormsModule din @ unghiulare / formulare și adăugați-o la matricea importurilor din app.module.ts.

app / app.module.ts

// Import import ReactiveFormsModule ReactiveFormsModule de la '@ unghiular / formulare'; @NgModule (... // Adăugați modulul importurilor Array imports: [BrowserModule, ReactiveFormsModule ...) clasa de export AppModule  

Apoi, creați un model de utilizator pentru formularul de înregistrare. Putem folosi fie o clasă, fie o interfață pentru crearea modelului. Pentru acest tutorial, voi exporta o clasă cu următoarele proprietăți.

app / User.ts

clasa de export User id: number; e-mail: șir; // Ambele parole sunt într-o singură parolă de obiect: pwd: string; confirmPwd: șir; ; sex: șir; termeni: boolean; constructor (valori: Object = ) // Initializare constructor Object.assign (acest lucru, valori);  

Acum, creați o instanță a modelului Utilizator în SignupForm component. 

app / înscriere / formularul de înscriere la form.component.ts

import Component, OnInit de la '@ angular / core'; // Importul modelului User import User din "./... / User"; @Component (selector: 'app-înscriere formă', templateUrl: './signup-form.component.html', styleUrls: [“./signup-form.component.css']) export clasă SignupFormComponent implementează OnInit // Lista de gen pentru elementul de control selectiv genderList: string []; // Proprietatea pentru utilizatorul privat de utilizator: User; ngOnInit () this.genderList = ['Bărbat', 'Femeie', 'Alții']; 

Pentru înscriere-form.component.html fișier, voi folosi același șablon HTML discutat mai sus, dar cu modificări minore. Formularul de înscriere are un câmp selectat cu o listă de opțiuni. Deși acest lucru funcționează, o vom face modul Angular prin introducerea în listă folosind ngFor directivă.

app / înscriere / formularul de înscriere la form.component.html

Inscrie-te...
...

Notă: Este posibil să primiți o eroare care spune Niciun furnizor pentru ControlContainer. Eroarea apare când o componentă are a

etichetă fără o directivă privind formule de grup. Eroarea va dispărea odată ce vom adăuga o directivă FormGroup mai târziu în tutorial.

Avem o componentă, un model și un șablon de formă la îndemână. Ce acum? Este timpul să ne murdărim mâinile și să ne cunoaștem API-urile de care aveți nevoie pentru a crea forme reactive. Aceasta include FormControl și FormGroup

Urmărirea stării folosind FormControl

În timp ce construiți formulare cu strategia formelor reactive, nu veți întâlni directivele ngModel și ngForm. În schimb, folosim API-ul FormControl și FormGroup.

Un FormControl este o direcție folosită pentru a crea o instanță FormControl pe care o puteți utiliza pentru a urmări starea unui anumit element de formă și starea sa de validare. Acesta este modul în care funcționează FormControl:

/ * Import FormControl prima * / import FormControl din '@ unghiular / formulare'; / * Exemplu de creare a unei noi instanțe FormControl * / class de export SignupFormComponent email = new FormControl (); 

e-mail este acum o instanță FormControl și o puteți lega de un element de control al intrării în șablon după cum urmează:

Inscrie-te

Elementul Formular șablon este acum legat de instanța FormControl din componentă. Ceea ce înseamnă că orice modificare a valorii de control a intrărilor se reflectă la celălalt capăt. 

Un constructor FormControl accepta trei argumente-o valoare inițială, o serie de validatoare de sincronizare, și o serie de async validatoare și după cum ați ghicit, toate sunt opționale. Vom prezenta primele două argumente aici. 

import Validatorii de la '@ unghiular / formulare'; ... / * FormControl cu ​​valoare inițială și validator * / email = nou FormControl ('[email protected] ', Validatori.required); 

Angular are un set limitat de validatori încorporați. Metodele de validare populare includ Validators.required, Validators.minLength, Validators.maxlength, și Validators.pattern. Cu toate acestea, pentru a le utiliza, trebuie să importați mai întâi API-ul Validator.

Pentru formularul nostru de înscriere, avem mai multe câmpuri de control al intrărilor (pentru email și parolă), un câmp selector și un câmp de casetă de selectare. Mai degrabă decât să creezi individ FormControl obiecte, nu ar fi mai logic să grupezi toate acestea FormControls într-o singură entitate? Acest lucru este benefic, deoarece putem urmări acum valoarea și valabilitatea tuturor obiectelor Sub-FormControl într-un singur loc. Asta e ceea ce FormGroup este pentru. Așadar, vom înregistra o grupă parentală FormGroup cu mai multe copii FormControls. 

Group FormControls multiple cu FormGroup

Pentru a adăuga o FormGroup, importați-o mai întâi. Apoi, declarați signupForm ca proprietate de clasă și inițializați-o după cum urmează:

app / înscriere / formularul de înscriere la form.component.ts

// Importați API-ul pentru a construi un formular de import FormControl, FormGroup, Validators din '@ unghiular / formulare'; clasa de export SignupFormComponent implementează OnInit genderList: String []; signupForm: FormGroup; ... ngOnInit () this.genderList = ["Bărbat", "Femeie", "Alții"]; this.signupForm = new FormGroup (email: new FormControl (“Validators.required), PWD: new FormControl (), confirmPwd: new FormControl (), gen: new FormControl (), termenii: new FormControl ()) 

Legați modelul FormGroup la DOM după cum urmează: 

app / înscriere / formularul de înscriere la form.component.html

  
Inscrie-te
...

[formGroup] = "signupForm" spune Angular că doriți să asociați acest formular cu FormGroup declarată în clasa componentei. Când vede Angular formControlName = "e-mail", verifică pentru o instanță a FormControl cu ​​valoarea cheie e-mail în grupul parental FormGroup. 

În mod similar, actualizați celelalte elemente de formă adăugând o formControlName = "valoare" așa cum am făcut aici.

Pentru a vedea dacă totul funcționează conform așteptărilor, adăugați următoarele după eticheta de formular:

app / înscriere / formularul de înscriere la form.component.html

 

Valoarea formularului signupForm.value | json

Starea formularului signupForm.status | json

Țeava SignupForm proprietate prin JsonPipe pentru a face modelul ca JSON în browser. Acest lucru este util pentru depanare și logare. Ar trebui să vedeți o ieșire JSON ca aceasta.

Există două lucruri de reținut aici:

  1. JSON nu se potrivește exact cu structura modelului de utilizator pe care l-am creat mai devreme. 
  2. signupForm.status afișează faptul că starea formularului este nevalidă. Acest lucru arată în mod clar că Validators.required pe câmpul de control al e-mailului funcționează conform așteptărilor. 

Structura modelului de model și a modelului de date ar trebui să se potrivească. 

// model de formular  "e-mail": "", "pwd": "", "confirmPwd": "", "gen": "", "termeni": false // model de utilizator  "e-mail": "" , "parola": "pwd": "", "confirmPwd": "", "gender": "

Pentru a obține structura ierarhică a modelului de date, ar trebui să folosim o FormGroup imbricată. În plus, este întotdeauna o idee bună să aveți elemente de formă asociate sub o singură FormGroup. 

Grupul de Forme Nomenclat

Creați o nouă FormGroup pentru parolă.

app / înscriere / formularul de înscriere la form.component.ts

 this.signupForm = nou FormGroup (email: nou FormControl (", Validators.required), parola: new FormGroup (pwd: new FormControl (), confirmPwd: : nou FormControl ())

Acum, pentru a lega modelul de formă nou cu DOM, efectuați următoarele modificări:

app / înscriere / formularul de înscriere la form.component.html

 

formGroupName = "parola" execută legarea pentru FormGroup imbricată. Acum, structura modelului de formă corespunde cerințelor noastre.

Valoarea formularului: "email": "", "parola": "pwd": null, "confirmPwd": null, "gender"

În continuare, trebuie să validăm controalele formularului.

Validarea formularului

Avem o validare simplă în locul controlului de intrare a mesajelor de e-mail. Cu toate acestea, nu este suficient. Iată întreaga listă a cerințelor noastre pentru validare.

  • Toate elementele de control ale formelor sunt necesar.
  • Dezactivați butonul Trimiteți până când starea formularului este VALIDĂ.
  • Câmpul de e-mail trebuie să conțină strict un id de e-mail.
  • Câmpul parolei trebuie să aibă o lungime minimă de 8.

Primul este ușor. Adăuga Validator.required la toate FormControls în modelul de formă. 

app / înscriere / formularul de înscriere la form.component.ts 

 this.signupForm = nou FormGroup (email: nou FormControl (", Validators.required), parola: new FormGroup (pwd: newContControl (" Validators.required "), confirmPwd: newContControl (" Validators.required) ), gen: nou FormControl (", Validators.required), // requiredTrue pentru ca câmpul terms să fie valabil numai dacă termenii verificați: new FormControl (", Validators.requiredTrue)

Apoi, dezactivați butonul în timp ce forma este INVALIDĂ.

app / înscriere / formularul de înscriere la form.component.html

 

Pentru a adăuga o constrângere la e-mail, puteți utiliza fie implicit Validators.email sau creați un personalizat Validators.pattern () care specifică expresii regulate ca cea de mai jos:

mail: FormControl nou (", [Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0-9.  $ ')])

Folosește MINLENGTH validator pentru câmpurile de parolă.

 parola: formularul nou (pwd: new FormControl (", [Validators.required, Validators.minLength (8)]), confirmPwd: new FormControl (", Validators.required, Validators.minLength (8),

Asta e pentru validare. Totuși, logica modelului de formă pare să fie aglomerată și repetitivă. Să o curățăm mai întâi. 

Refactorizarea codului folosind FormBuilder

Angular vă oferă un gest de sintaxă pentru crearea de noi instanțe ale FormGroup și FormControl numite FormBuilder. Formularul API FormBuilder nu face altceva decât altceva decât ceea ce ne-am referit aici.

Ea simplifică codul nostru și face procesul de a construi o formă ușor pe ochi. Pentru a crea un FormBuilder, trebuie să îl importați înscriere form.component.ts și injectați FormBuilder-ul în constructor.

app / înscriere / formularul de înscriere la form.component.ts 

importați FormBuilder, FormGroup, Validatori din '@ unghiular / formulare'; ... clasa de export SignupFormComponent implementează OnInit signupForm: FormGroup; // Declarați formularul signupForm // Injectați formbuilderul în constructorul constructorului (privat fb: FormBuilder)  ngOnInit () ...

În loc de a crea un nou FormGroup (), noi folosim this.fb.group pentru a construi o formă. Cu excepția sintaxei, tot restul rămâne același.

app / înscriere / formularul de înscriere la form.component.ts 

 ngOnInit () ... this.signupForm = this.fb.group (email: [", [Validators.required, Validators.pattern ('[a-z0-9 ._% +-] + @ [a-z0- (Pwd: [", [Validators.required, Validators.minLength (8)]], confirmați Pwd : [", [Validators.required, Validators.minLength (8)]]), sex: [", Validatori.required], termeni: [", Validators.requiredTrue]

Afișarea erorilor de validare 

Pentru a afișa erorile, voi folosi directiva condiționată ngIf pe un element div. Să începem cu câmpul de control al intrărilor pentru e-mail:

 

Există câteva probleme aici. 

  1. Unde a fost invalid și impecabilă vine din? 
  2. signupForm.controls.email.invalid este prea lungă și adâncă.
  3. Eroarea nu explică în mod explicit de ce este nevalidă.

Pentru a răspunde la prima întrebare, fiecare FormControl are anumite proprietăți invalid, valabil, impecabilă, murdar, atins, și neatins. Putem folosi aceste pentru a determina dacă un mesaj de eroare sau un avertisment ar trebui să fie afișate sau nu. Imaginea de mai jos descrie în detaliu fiecare dintre aceste proprietăți.

Deci elementul div cu * ngIf va fi redat numai dacă e-mailul este nevalid. Cu toate acestea, utilizatorul va fi întâmpinat cu erori cu privire la faptul că câmpurile de intrare sunt goale chiar înainte de a avea șansa de a edita formularul. 

Pentru a evita acest scenariu, am adăugat cea de-a doua condiție. Eroarea va fi afișată numai după controlul a fost vizitat.

Pentru a scăpa de lanțul lung de nume de metode (signupForm.controls.email.invalid), Voi adăuga câteva metode de stenogramă. Acest lucru le face mai accesibile și mai scurte. 

app / înscriere / formularul de înscriere la form.component.ts 

clasa de export SignupFormComponent implementează OnInit ... primi email () return this.signupForm.get ('email');  parola () return this.signupForm.get ("parola");  a obține gen () return this.signupForm.get ('gender');  obține termeni () return this.signupForm.get ("termeni"); 

Pentru a face eroarea mai explicită, am adăugat condițiile de img imf mai jos:

app / înscriere / formularul de înscriere la form.component.html

 
Câmpul de e-mail nu poate fi gol
Id-ul de e-mail nu pare corect

Folosim email.errors pentru a verifica toate erorile posibile de validare și apoi a le afișa înapoi utilizatorilor sub formă de mesaje personalizate. Acum urmați aceeași procedură pentru celelalte elemente de formă. Iată cum am codificat validarea parolelor și controlul de introducere a termenilor.

app / înscriere / formularul de înscriere la form.component.html

  
Parola trebuie să aibă mai mult de 8 caractere
...
Vă rugăm să acceptați mai întâi Termenii și condițiile.

Trimiteți formularul utilizând ngSubmit

Aproape că am terminat cu formularul. Nu îi lipsește funcționalitatea prezentată, pe care urmează să o implementăm acum.

La trimiterea formularului, valorile modelului de model ar trebui să curgă în proprietatea utilizatorului componentei.

app / înscriere / formularul de înscriere la form.component.ts

public onFormSubmit () dacă (acest.signupForm.valid) this.user = this.signupForm.value; console.log (this.user); / * Orice logica de apel API prin servicii merge aici * /

Înfășurați-o

Dacă ați urmat această serie de tutori de la bun început, am avut o experiență practică cu două tehnologii populare de construcție a formelor în Angular. Tehnicile bazate pe șablon și bazate pe modele reprezintă două modalități de realizare a aceluiași lucru. Personal, prefer să folosesc formele reactive din următoarele motive:

  • Toate logica de validare a formularului va fi localizată într-un singur loc - în interiorul clasei componente. Acest lucru este mult mai productiv decât abordarea șablonului, unde directivele ngModel sunt împrăștiate în șablon.
  • Spre deosebire de formularele bazate pe șabloane, formele bazate pe model sunt mai ușor de testat. Nu trebuie să recurgeți la biblioteci de testare end-to-end pentru a testa formularul.
  • Lansarea validării va intra în clasa componentei și nu în șablon.
  • Pentru un formular cu un număr mare de elemente de formă, această abordare are ceva numit FormBuilder pentru a facilita crearea obiectelor FormControl.

Ne-am lipsit de un singur lucru, și asta scrie un validator pentru nepotrivirea parolei. În partea finală a seriei, vom acoperi tot ce trebuie să știți despre crearea funcțiilor validatoare personalizate în Angular. Rămâi acordat până atunci.

Între timp, există o mulțime de cadre și biblioteci pentru a vă menține ocupat, cu o mulțime de articole pe Envato Market pentru a citi, a studia și a utiliza.

Cod