Inleiding tot vormen in hoek 4 reactieve vormen

Wat je gaat creëren

Dit is het tweede deel van de serie over Inleiding tot Formulieren in Angular 4. In het eerste deel hebben we een formulier gemaakt met behulp van de sjabloongestuurde aanpak. We gebruikten richtlijnen zoals ngModel, ngModelGroup en ngForm om de formulierelementen op te laden. In deze zelfstudie gaan we een andere benadering gebruiken voor het bouwen van vormen, de reactieve manier. 

Reactieve vormen

Reactieve vormen hebben een andere benadering dan die van de sjabloongestuurde vormen. Hier maken en initialiseren we de vorm controle objecten in onze componentklasse. Het zijn tussenliggende objecten die de status van het formulier behouden. We zullen ze dan binden aan de vorm besturingselementen in de sjabloon.

Het formulierbesturingsobject luistert naar elke wijziging in de invoerbesturingswaarden en deze worden onmiddellijk weerspiegeld in de staat van het object. Omdat de component directe toegang heeft tot de gegevensmodelstructuur, kunnen alle wijzigingen worden gesynchroniseerd tussen het gegevensmodel, het formulierbesturingsobject en de invoerbesturingswaarden. 

Praktisch gesproken, als we een formulier bouwen voor het bijwerken van het gebruikersprofiel, is het datamodel het gebruikersobject dat is opgehaald van de server. Volgens afspraak wordt dit vaak opgeslagen in de gebruikerseigenschap van de component (this.user). Het formulierbesturingsobject of het formuliermodel zal worden gebonden aan de eigenlijke formulierbesturingselementen van de sjabloon.

Beide modellen moeten vergelijkbare structuren hebben, zelfs als ze niet identiek zijn. De invoerwaarden mogen echter niet rechtstreeks in het gegevensmodel vloeien. De afbeelding beschrijft hoe de gebruikersinvoer vanuit de sjabloon zijn weg vindt naar het formuliermodel.

Laten we beginnen.

voorwaarden

U hoeft deel 1 van deze serie niet te hebben gevolgd, want deel twee is logisch. Als u echter nog geen ervaring heeft met formulieren in Angular, raad ik u aan de sjabloongestuurde strategie te doorlopen. De code voor dit project is beschikbaar op mijn GitHub-repository. Zorg ervoor dat u zich in de juiste tak bevindt en download vervolgens de zip of, als alternatief, de repo om het formulier in actie te zien klonen. 

Als u liever vanuit nul begint, zorg er dan voor dat u Angular CLI hebt geïnstalleerd. Gebruik de ng opdracht om een ​​nieuw project te genereren. 

$ ng nieuw SignupFormProject

Maak vervolgens een nieuw component voor de SignupForm of maak er handmatig een. 

ng component Installatievorm genereren

Vervang de inhoud van app.component.html hiermee:

 

Hier is de mapstructuur voor de src / directory. Ik heb een aantal niet-essentiële bestanden verwijderd om de dingen eenvoudig te houden.

. ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── inschrijfformulier │ │ ├ ── signup-form.component.css │ │ ├── signup-form.component.html │ │ └── signup-form.component.ts │ └── User.ts ├── index.html ├── main .ts ├── polyfills.ts ├── styles.css ├── tsconfig.app.json └── typings.d.ts 

Zoals je kunt zien, een map voor de SignupForm component is automatisch gemaakt. Dat is waar het grootste deel van onze code zal gaan. Ik heb ook een nieuw gemaakt User.ts voor het opslaan van ons gebruikersmodel.

De HTML-sjabloon

Voordat we in de eigenlijke componentsjabloon duiken, moeten we een abstract idee hebben van wat we aan het bouwen zijn. Dus hier is de vormstructuur die ik in gedachten heb. Het aanmeldingsformulier heeft meerdere invoervelden, een select element en een checkbox-element. 


Hier is de HTML-sjabloon die we zullen gebruiken voor onze registratiepagina. 

HTML-sjabloon

 
Inschrijven

De CSS-klassen die in de HTML-sjabloon worden gebruikt, maken deel uit van de Bootstrap-bibliotheek die wordt gebruikt om dingen mooier te maken. Omdat dit geen zelfstudie is, zal ik niet veel over de CSS-aspecten van de vorm praten, tenzij dat nodig is. 

Basic Form Setup

Om een ​​Reactief formulier te maken, moet u het ReactiveFormsModule van @ Hoekige / formulieren en voeg het toe aan de invoerarray in app.module.ts.

app / app.module.ts

// Importeer ReactiveFormsModule import ReactiveFormsModule uit '@ angular / forms'; @ NgModule (... // Voeg de module toe aan de invoer Array-import: [BrowserModule, ReactiveFormsModule ...) exportklasse AppModule  

Maak vervolgens een gebruikersmodel voor het registratieformulier. We kunnen een klasse of een interface gebruiken om het model te maken. Voor deze zelfstudie ga ik een klasse exporteren met de volgende eigenschappen.

app / User.ts

exportklasse Gebruiker id: number; e-mail: string; // Beide wachtwoorden bevinden zich in een enkel objectwachtwoord: pwd: string; confirmPwd: string; ; geslacht: string; voorwaarden: boolean; constructor (waarden: Object = ) // Constructor-initialisatie Object.assign (this, values);  

Maak nu een exemplaar van het gebruikersmodel in de SignupForm bestanddeel. 

app / aanmelden-form / aanmelden-form.component.ts

importeer Component, OnInit uit '@ angular / core'; // Importeer het gebruikersmodel importeren Gebruiker uit './... / Gebruiker'; @Component (selector: 'app-signup-formulier', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css']) exporteer class SignupFormComponent implementeert OnInit // Geslachtlijst voor het geselecteerde besturingselement private genderList: string []; // Eigenschap voor de gebruiker Privégebruiker: Gebruiker; ngOnInit () this.genderList = ['Mannelijk', 'Vrouw', 'Andere']; 

Voor de aanmelden-form.component.html bestand, ga ik dezelfde HTML-sjabloon gebruiken die hierboven is besproken, maar met kleine wijzigingen. Het aanmeldingsformulier heeft een select veld met een lijst met opties. Hoewel dat werkt, doen we het op de hoekige manier door de lijst door te lopen met behulp van de ngFor richtlijn.

app / aanmelden-form / aanmelden-form.component.html

Inschrijven...
...

Opmerking: mogelijk krijgt u een foutmelding Geen provider voor ControlContainer. De fout verschijnt wanneer een component een heeft

tag zonder een formulierGroup-richtlijn. De fout verdwijnt als we later in de zelfstudie een FormGroup-richtlijn toevoegen.

We hebben een component, een model en een formuliersjabloon bij de hand. Wat nu? Het is tijd om onze handen vuil te maken en kennis te maken met de API's die u nodig hebt om reactieve vormen te creëren. Dit bevat FormControl en FormGroup

Het volgen van de staat met behulp van FormControl

Terwijl u formulieren bouwt met de strategie voor reactieve vormen, ziet u de instructies ngModel en ngForm niet. In plaats daarvan gebruiken we de onderliggende API FormControl en FormGroup.

Een FormControl is een instructie die wordt gebruikt om een ​​FormControl-instantie te maken die u kunt gebruiken om de status van een bepaald formulierelement en de bijbehorende validatiestatus bij te houden. Dit is hoe FormControl werkt:

/ * Import FormControl eerst * / import FormControl vanuit '@ angular / forms'; / * Voorbeeld van het maken van een nieuwe FormControl-instantie * / exportklasse SignupFormComponent email = new FormControl (); 

e-mail is nu een FormControl-instantie en u kunt het als volgt binden aan een invoerbesturingselement in uw sjabloon:

Inschrijven

Het sjabloonformulierelement is nu gebonden aan de FormControl-instantie in het onderdeel. Wat dat betekent, is dat elke verandering in de invoerbesturingswaarde aan het andere uiteinde wordt weerspiegeld. 

Een FormControl-constructor accepteert drie argumenten - een beginwaarde, een reeks synchronisatievalidators en een reeks asynchrone validators - en zoals u misschien al hebt geraden, zijn ze allemaal optioneel. We zullen hier de eerste twee argumenten behandelen. 

importeer Validators uit '@ angular / forms'; ... / * FormControl met beginwaarde en een validator * / email = nieuwe FormControl ('[email protected] ', Validators.required); 

Angular heeft een beperkte set ingebouwde validators. De populaire validatiemethoden omvatten Validators.required, Validators.minLength, Validators.maxlength, en Validators.pattern. Om ze te gebruiken, moet u echter eerst de Validator API importeren.

Voor ons aanmeldingsformulier hebben we meerdere invoerbesturingsvelden (voor e-mail en wachtwoord), een selectieveld en een selectievakje. In plaats van een individu te creëren FormControl objecten, zou het niet logischer zijn om al deze te groeperen FormControls onder een enkele entiteit? Dit is nuttig omdat we nu de waarde en de geldigheid van alle sub-FormControl-objecten op één plaats kunnen volgen. Dat is wat FormGroup is voor. Dus we zullen een bovenliggende FormGroup registreren met meerdere onderliggende FormControls. 

Group Multiple FormControls met FormGroup

Als u een FormGroup wilt toevoegen, moet u deze eerst importeren. Formuleer vervolgens signupForm als een klasseneigenschap en initialiseer het als volgt:

app / aanmelden-form / aanmelden-form.component.ts

// Importeer de API voor het bouwen van een formulierimport FormControl, FormGroup, Validators uit '@ angular / forms'; export class SignupFormComponent implementeert OnInit genderList: String []; signupForm: FormGroup; ... ngOnInit () this.genderList = ['Male', 'Female', 'Others']; this.signupForm = nieuwe FormGroup (email: new FormControl (", Validators.required), pwd: new FormControl (), confirmPwd: new FormControl (), gender: new FormControl (), terms: new FormControl ()) 

Bind het FormGroup-model als volgt aan de DOM: 

app / aanmelden-form / aanmelden-form.component.html

  
Inschrijven
...

[formGroup] = "signupForm" vertelt Angular dat u dit formulier wilt koppelen aan de FormGroup aangegeven in de componentklasse. Wanneer Angular ziet formControlName = "email", het controleert een instantie van FormControl met de sleutelwaarde e-mail in de bovenliggende FormGroup. 

Werk op dezelfde manier de andere formulierelementen bij door een a toe te voegen formControlName = "value" attribuut zoals we net hier deden.

Om na te gaan of alles naar behoren werkt, voegt u het volgende toe na de formuletag:

app / aanmelden-form / aanmelden-form.component.html

 

Formulierwaarde signupForm.value | json

Formulierstatus signupForm.status | json

Leid de SignupForm eigendom door de JsonPipe om het model als JSON in de browser weer te geven. Dit is handig voor foutopsporing en logging. Je zou een JSON-uitgang zoals deze moeten zien.

Er zijn twee dingen om op te merken:

  1. De JSON komt niet precies overeen met de structuur van het gebruikersmodel dat we eerder hebben gemaakt. 
  2. De signupForm.status geeft aan dat de status van het formulier ONGELDIG is. Dit toont duidelijk aan dat het Validators.required op het veld voor e-mailbesturing werkt zoals verwacht. 

De structuur van het formuliermodel en het gegevensmodel moeten overeenkomen. 

// Formulier model "email": "", "pwd": "", "confirmPwd": "", "gender": "", "terms": false // Gebruikersmodel "email": "" , "password": "pwd": "", "confirmPwd": "",, "gender": "", "terms": false

Om de hiërarchische structuur van het gegevensmodel te krijgen, moeten we een geneste FormGroup gebruiken. Bovendien is het altijd een goed idee om verwante formulierelementen onder een enkele FormGroup te hebben. 

Geneste FormGroup

Maak een nieuwe FormGroup voor het wachtwoord.

app / aanmelden-form / aanmelden-form.component.ts

 this.signupForm = nieuwe FormGroup (email: new FormControl (", Validators.required), password: new FormGroup (pwd: new FormControl (), confirmPwd: new FormControl ()), gender: new FormControl (), terms : nieuwe FormControl ())

Om het nieuwe formuliermodel te binden met de DOM, moet u de volgende wijzigingen aanbrengen:

app / aanmelden-form / aanmelden-form.component.html

 

formGroupName = "wachtwoord" voert de binding uit voor de geneste FormGroup. De structuur van het formuliermodel komt nu overeen met onze vereisten.

Vormwaarde: "email": "", "wachtwoord": "pwd": null, "confirmPwd": null, "gender": null, "terms": null Formulierstatus "INVALID"

Vervolgens moeten we de formulierbesturingselementen valideren.

Het formulier valideren

We hebben een eenvoudige validatie voor de controle van de e-mailinvoer. Dat is echter niet voldoende. Hier is de volledige lijst van onze vereisten voor de validatie.

  • Alle formulierbesturingselementen zijn verplicht.
  • Schakel de knop Verzenden uit totdat de status van het formulier GELDIG is.
  • Het e-mailveld moet een e-mail-ID bevatten.
  • Het wachtwoordveld moet een minimale lengte van 8 hebben.

De eerste is eenvoudig. Toevoegen Validator.required voor alle FormControls in het formuliermodel. 

app / aanmelden-form / aanmelden-form.component.ts 

 this.signupForm = nieuwe FormGroup (email: new FormControl (", Validators.required), password: new FormGroup (pwd: new FormControl (", Validators.required), confirmPwd: new FormControl (", Validators.required) ), geslacht: nieuwe FormControl (", Validators.required), // requiredTrue zodat het veld terms alleen geldig is als gecontroleerde termen: new FormControl (", Validators.requiredTrue))

Schakel vervolgens de knop uit terwijl het formulier ONGELDIG is.

app / aanmelden-form / aanmelden-form.component.html

 

Om een ​​beperking op e-mail toe te voegen, kunt u de standaard gebruiken Validators.email of maak een aangepast Validators.pattern () die reguliere expressies specificeert zoals die hieronder:

email: new FormControl (", [Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0-9 .-] + \. [az] 2,3  $ ')])

Gebruik de minimale lengte validator voor de wachtwoordvelden.

 wachtwoord: nieuwe FormGroup (pwd: nieuwe FormControl (", [Validators.required, Validators.minLength (8)]), confirmPwd: new FormControl (", [Validators.required, Validators.minLength (8)])),

Dat is het voor de validatie. De logica van het formuliermodel lijkt echter rommelig en repetitief. Laten we dat eerst opruimen. 

De code opnieuw formuleren met behulp van FormBuilder

Angular biedt u een syntaxisuiker voor het maken van nieuwe exemplaren van FormGroup en FormControl, genaamd FormBuilder. De FormBuilder-API doet niets bijzonders behalve wat we hier hebben behandeld.

Het vereenvoudigt onze code en maakt het proces van het bouwen van een formulier eenvoudig voor de ogen. Als u een FormBuilder wilt maken, moet u deze importeren aanmelden-form.component.ts en injecteer de FormBuilder in de constructor.

app / aanmelden-form / aanmelden-form.component.ts 

import FormBuilder, FormGroup, Validators van '@ angular / forms'; ... exporteer class SignupFormComponent implementeert OnInit signupForm: FormGroup; // Declare the signupForm // Injecteer de formbuilder in de constructor constructor (private fb: FormBuilder)  ngOnInit () ...

In plaats van een nieuw te maken FormGroup (), wij gebruiken this.fb.group om een ​​formulier te bouwen. Met uitzondering van de syntaxis blijft al het andere hetzelfde.

app / aanmelden-form / aanmelden-form.component.ts 

 ngOnInit () ... this.signupForm = this.fb.group (email: [", [Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0- 9 .-] + \. [Az] 2,3 $ ')]], wachtwoord: this.fb.group (pwd: [", [Validators.required, Validators.minLength (8)]], confirmPwd : [", [Validators.required, Validators.minLength (8)]]), geslacht: [", Validators.required], terms: [", Validators.requiredTrue])

Validatiefouten weergeven 

Voor het weergeven van de fouten ga ik de voorwaardelijke richtlijn gebruiken ngIf op een div-element. Laten we beginnen met het invoerveld voor e-mail:

 

Er zijn een paar problemen hier. 

  1. Waar ging ongeldig en ongerept Komt van? 
  2. signupForm.controls.email.invalid is te lang en diep.
  3. De fout geeft niet expliciet aan waarom het ongeldig is.

Om de eerste vraag te beantwoorden, heeft elke FormControl bepaalde eigenschappen zoals ongeldig, Geldig, ongerept, vuil, aangeraakt, en onaangeroerd. We kunnen deze gebruiken om te bepalen of een foutmelding of een waarschuwing moet worden weergegeven of niet. De onderstaande afbeelding beschrijft elk van die eigenschappen in detail.

Dus het div element met de * ngIf wordt alleen weergegeven als de e-mail ongeldig is. De gebruiker zal echter worden begroet met fouten over de lege invulvelden, zelfs voordat ze de kans hebben om het formulier te bewerken. 

Om dit scenario te voorkomen, hebben we de tweede voorwaarde toegevoegd. De fout wordt pas daarna weergegeven de besturing is bezocht.

Om zich te ontdoen van de lange keten van methoden namen (signupForm.controls.email.invalid), Ik ga een aantal methoden voor het toevoegen van verkorte methoden toevoegen. Hierdoor blijven ze toegankelijker en korter. 

app / aanmelden-form / aanmelden-form.component.ts 

export class SignupFormComponent implementeert OnInit ... get email () retourneer dit.signupForm.get ('email');  krijg wachtwoord () retourneer dit.signupForm.get ('wachtwoord');  krijg geslacht () return this.signupForm.get ('geslacht');  krijg termen () retourneer this.signupForm.get ('terms'); 

Om de fout meer expliciet te maken, heb ik hieronder geneste ngIf-voorwaarden toegevoegd:

app / aanmelden-form / aanmelden-form.component.html

 
Het e-mailveld mag niet leeg zijn
Het e-mailadres lijkt niet goed

We gebruiken email.errors om alle mogelijke validatiefouten te controleren en deze vervolgens weer te geven aan de gebruiker in de vorm van aangepaste berichten. Volg nu dezelfde procedure voor de andere formulierelementen. Hier is hoe ik de validatie voor de wachtwoorden en de termen invoerbesturing heb gecodeerd.

app / aanmelden-form / aanmelden-form.component.html

  
Wachtwoord moet uit meer dan 8 tekens bestaan
...
Accepteer alstublieft eerst de Algemene voorwaarden.

Dien het formulier in met behulp van ngSubmit

We zijn bijna klaar met de vorm. Het ontbreekt aan de submit-functionaliteit, die we nu gaan implementeren.

Bij het verzenden van formulieren moeten de formuliermodelwaarden naar de gebruikseigenschap van de component vloeien.

app / aanmelden-form / aanmelden-form.component.ts

public onFormSubmit () if (this.signupForm.valid) this.user = this.signupForm.value; console.log (this.user); / * Elke API-oproeplogica via services gaat hier * /

Inpakken

Als je deze tutorialserie vanaf het begin hebt gevolgd, hadden we een praktische ervaring met twee populaire vormgevingstechnologieën in Angular. De sjabloongestuurde en modelgestuurde technieken zijn twee manieren om hetzelfde te bereiken. Persoonlijk gebruik ik de reactieve vormen om de volgende redenen:

  • Alle logica voor formuliervalidatie bevindt zich op één plaats: in uw componentklasse. Dit is veel productiever dan de sjabloonaanpak, waarbij de ngModel-richtlijnen over de sjabloon zijn verspreid.
  • In tegenstelling tot sjabloongestuurde formulieren zijn modelgestuurde formulieren eenvoudiger te testen. U hoeft geen toevlucht te nemen tot end-to-end testbibliotheken om uw formulier te testen.
  • Validatie-logica zal binnen de componentklasse gaan en niet in de sjabloon.
  • Voor een formulier met een groot aantal formulierelementen heeft deze benadering de naam FormBuilder om het maken van FormControl-objecten eenvoudiger te maken.

We hebben één ding gemist en dat is het schrijven van een validator voor het misaanpassing van het wachtwoord. In het laatste deel van de serie bespreken we alles wat u moet weten over het maken van aangepaste validatiefuncties in Angular. Blijf op de hoogte tot dan.

In de tussentijd zijn er genoeg kaders en bibliotheken om je bezig te houden, met veel items op Envato Market om te lezen, te bestuderen en te gebruiken.