Introduksjon til skjemaer i vinkel 4 Skrive tilpassede skjema validatorer

Dette er den tredje delen av serien om å skape former i Angular. I de to første opplæringen brukte vi Angular sin mal-drevne og modelldrevne tilnærming til å lage skjemaer. Men mens du detaljerte begge tilnærmingene, var det noe vi ikke dekker - tilpassede valideringsfunksjoner. Denne opplæringen vil dekke alt du trenger å vite om å skrive tilpassede validatorer som oppfyller dine krav.

Forutsetninger

Du trenger ikke å følge del 1 eller 2 i denne serien for del tre for å gi mening. Men hvis du er helt ny til skjemaer i Angular, bør du gå over til den første opplæringen i denne serien og starte derfra. 

Ellers ta en kopi av denne koden fra vår GitHub repo og bruk det som utgangspunkt.  

Innebygde Validatorer

Angular har ikke et stort innebygd validatorbibliotek. Fra Angular 4 har vi følgende populære validatorer i Angular:

  • nødvendig
  • minlength
  • maks lengde
  • mønster

Det er faktisk noen få flere, og du kan se hele listen i Angular docs. 

Vi kan bruke de ovennevnte innebygde validatorene på to måter:

1. Som direktiver i mal-drevne former.

2. Som validatorer inne i FormControl konstruktør i modelldrevne former.

name = new FormControl (", Validators.required) 

Hvis synspunktet ovenfor ikke gir mening, følger du mine tidligere veiledninger om å bygge et registreringsskjema ved hjelp av en malstyrt tilnærming eller en modelldrevet tilnærming, og deretter slippe tilbake!

De innebygde skjemavalidatorene dekker ikke alle de valideringsbrukstilfeller som kan være påkrevd i en virkelig applikasjon. For eksempel kan et registreringsskjema kanskje sjekke om verdiene til passordet og bekreft passordkontrollfeltene er like og viser en feilmelding hvis de ikke samsvarer. En validator som svartelister e-post fra et bestemt domene er et annet vanlig eksempel. 

Her er et faktum: Template-drevne skjemaer er bare modelldrevne skjemaer under. I en mal-drevet form lar vi malen ta vare på modellopprettelsen for oss. Det åpenbare spørsmålet nå er, hvordan legger du til en validator i et skjema?

Validatorer er bare funksjoner. I en modelldrevet form er vedlegg av validatorer til FormControl rettferdig. I en mal-drevet form er det imidlertid litt mer arbeid å gjøre. I tillegg til validatorfunksjonen må du skrive et direktiv for validatoren og opprette forekomster av direktivet i malen.

Dykking inn i detaljene

Selv om dette allerede er dekket, vil vi gå gjennom en rask omtale av koden for registreringsskjemaet. Først, her er den reaktive tilnærmingen.

app / registrering-skjema / registrerings-form.component.ts

 // Bruk formbyggeren til å bygge Form-modellen this.signupForm = this.fb.group (email: [", [Validators.required, Validators.pattern ('[a-z0-9 ._% + -] + @ [a-z0-9 .-] + \. [az] 2,3 $ ')]], passord: this.fb.group (pwd: [", [Validators.required, Validators.minLength )], bekreftePwd: [", [Validators.required, Validators.minLength (8)]], validator: PasswordMatch), kjønn: [" Validators.required ",)

FormBuilder er en syntaks sukker som skaper FormGroup og FormControl forekomster. EN FormControl sporer verdien og valideringsstatusen til et enkelt formelement. EN FormGroup, på den annen side, omfatter en gruppe av FormControl forekomster, og det sporer verdien og gyldigheten av hele gruppen.

Her er strukturen som vi har fulgt:

FormGroup -> 'signupForm' FormControl -> 'email' FormGroup -> 'passord' FormControl -> 'pwd' FormControl -> 'confirmPwd' FormControl -> 'gender' 

Avhengig av kravene, kan vi legge ved en validator til en FormControl eller a FormGroup. En e-post blacklisting validator ville kreve at den ble festet til FormControl forekomst av e-posten. 

Men for mer komplekse valideringer der flere kontrollfelt må sammenlignes og validert, er det en bedre ide å legge til valideringslogikken for foreldrene FormGroup. Som du kan se, passord har en FormGroup av seg selv, og dette gjør det enkelt for oss å skrive validatorer som kontrollerer likestilling av pwd og confirmPwd.

For det malformede skjemaet går all den logikken inn i HTML-malen, og her er et eksempel:

app / registrering-skjema / registrering-form.component.html

...

ngModel skaper en forekomst av FormControl og binder den til et skjemakontrollelement. på samme måte, ngModelGroup skaper og binder en FormGroup eksempel på et DOM-element. De deler samme modelldomenestruktur som diskutert ovenfor. 

Det er også interessant å merke seg det FormControl, FormGroup, og FormArray utvide AbstractControl klasse. Hva dette betyr er at AbstractControl klassen er ansvarlig for å spore verdiene for skjemobjekter, validere dem og drive andre ting som uberørte, skitne og berørte metoder. 

Nå som vi er kjent med både formteknikkene, la oss skrive vår første tilpassede validator.

Tilpasset valideringsfunksjon for modelldrevne skjemaer

Validatorer er funksjoner som tar en FormControl/FormGroup eksempel som input og returnere heller null eller et feilobjekt. null returneres når valideringen er vellykket, og hvis ikke, blir feilobjektet kastet. Her er en veldig grunnleggende versjon av en valideringsfunksjon. 

app / passord-match.ts

importer FormGroup fra '@ vinkel / former'; eksportfunksjon passordMatch (kontroll: FormGroup): [nøkkel: streng]: boolsk 

Jeg har erklært en funksjon som aksepterer en forekomst av FormGroup som en inngang. Den returnerer et objekt med en nøkkel av typen streng og en ekte / falsk verdi. Dette er slik at vi kan returnere et feilobjekt av skjemaet nedenfor:

mismatch: true

Deretter må vi få verdien av pwd og confirmPwd FormControl-forekomster. Jeg skal bruke control.get () å hente sine verdier. 

eksportfunksjon passordMatch (kontroll: FormGroup): [nøkkel: streng]: boolsk // Grab pwd og confirmPwd ved hjelp control.get const pwd = control.get ('pwd'); const confirmPwd = control.get ('confirmPwd');  

Nå må vi gjøre sammenligningen og returnere enten null eller et feilobjekt.

app / passord-match.ts

importer AbstractControl fra '@ angular / forms'; eksportfunksjon passwordMatch (kontroll: AbstractControl): [nøkkel: streng]: boolean // Grab pwd og confirmPwd ved hjelp control.get const pwd = control.get ('pwd'); const confirmPwd = control.get ('confirmPwd'); // Hvis FormControl-objekter ikke eksisterer, return null hvis (! Pwd ||! ConfirmPwd) returnere null; // Hvis de faktisk er like, returner null hvis (pwd.value === confirmPwd.value) return null;  // Else returnere falsk retur mismatch: true;  

Hvorfor erstattet jeg FormGroup med AbstractControl? Som du vet, AbstractControl er mor til alle Form * klasser, og det gir deg mer kontroll over formkontrollobjektene. Det har den ekstra fordelen at den gjør vår valideringskode mer konsistent.

Importer passwordMatch fungere i SignupForm komponent og erklære det som en validator for passordet FormGroup forekomst.

app / passord-match.ts

importere passwordMatch fra './.../password-match'; ... eksportklasse SignupFormComponent implementerer OnInit ngOnInit () // Bruk formbuilderen til å bygge Form-modellen this.signupForm = this.fb.group (... passord: this.fb.group (pwd: [", [Validators.required, Validators.minLength (8)]], confirmPwd: [", [Validators.required, Validators.minLength (8)]], validator: passwordMatch ), ...) 

Viser feilene

Hvis du gjorde alt riktig, password.errors? .mismatch vil være sant når verdiene til begge feltene ikke stemmer overens.

password.errors? .mismatch json

Selv om det finnes alternative måter å vise feil på, skal jeg bruke ngIf direktiv for å avgjøre om en feilmelding skal vises eller ikke.

Først skal jeg bruke ngIf for å se om passordet er ugyldig. 

  

Vi bruker password.touched for å sikre at brukeren ikke blir møtt med feil selv før en tast er trykket.

Deretter skal jeg bruke ngIf = "uttrykket, så en annen b" syntaks for å vise den riktige feilen.

app / registrering-skjema / registrering-form.component.html

    Passordet stemmer ikke overens   Passordet må være over 8 tegn 

Der har du det, en fungerende modell av validatoren som sjekker om likestilling av passord.

Demo for tilpassede validatorer i modelldrevne skjemaer

Egendefinert Validator Directive for Template Driven Forms

Vi bruker den samme validatorfunksjonen som vi opprettet for det modelldrevne skjemaet tidligere. Vi har imidlertid ikke direkte tilgang til forekomster av FormControl/FormGroup i en mal-drevet form. Her er de tingene du må gjøre for å få validatoren til å fungere:

  1. Lage en PasswordMatchDirective som fungerer som en wrapper rundt passwordMatch validator funksjon. Vi registrerer direktivet som en validator ved hjelp av NG_VALIDATORS forsørger. Mer om dette senere.
  2. Fest direktivet til skjemakontrollen for skjemaet. 

La oss skrive direktivet først. Slik ser et direktiv ut i Angular:

app / passord-match.ts

importer AbstractControl fra '@ angular / forms'; eksportfunksjon passwordMatch (kontroll: AbstractControl): [nøkkel: streng]: boolean // Grab pwd og confirmPwd ved hjelp control.get const pwd = control.get ('pwd'); const confirmPwd = control.get ('confirmPwd'); // Hvis FormControl-objekter ikke eksisterer, return null hvis (! Pwd ||! ConfirmPwd) returnere null; // Hvis de faktisk er like, returner null hvis (pwd.value === confirmPwd.value) return null;  // Else returnere falsk retur mismatch: true;  // PasswordMatchDirective @Directive (selector: ", leverandører: []) eksportklasse PasswordMatchDirective 

De @Directive dekoratør brukes til å markere klassen som et vinkeldirektiv. Den aksepterer et objekt som et argument som spesifiserer direktivkonfigurasjonsmetadataene, for eksempel valggivere som direktivet skal festes til, og listen over leverandører som skal injiseres, etc. La oss fylle ut metadataene i direktivet:

app / passord-match.ts

@Directive (selector: '[passwordMatch] [ngModelGroup]', // 1 leverandører: [// 2 provide: NG_VALIDATORS, useValue: passwordMatch, multi: true]) eksportklasse PasswordMatchDirective 
  1. Direktivet er nå knyttet til alle inngangskontroller som har attributter ngModelGroup og passwordMatch
  2. Vi utvider de innebygde validatorene ved hjelp av NG_VALIDATORS forsørger. Som tidligere nevnt, NG_VALIDATORS er en leverandør som har en utvidbar samling av validatorer. De passwordMatch funksjonen som vi opprettet tidligere blir erklært som en avhengighet. De multi: sant Setter denne leverandøren til å være en multi-leverandør. Hva dette betyr er at vi vil legge til den eksisterende samlingen av validatorer levert av NG_VALIDATORS.

Nå legger du til direktivet i deklarasjonene i ngModule.

app / app.module.ts

... importere PasswordMatchDirective fra './password-match'; @NgModule (declarations: [AppComponent, SignupFormComponent, PasswordMatchDirective], import: [BrowserModule, FormsModule], leverandører: [], bootstrap: [AppComponent]) eksportklasse AppModule  

Viser feilmeldinger

For å vise valideringsfeilmeldingene, skal jeg bruke den samme malen som vi opprettet for modelldrevne skjemaer.

 
Passordet stemmer ikke overens Passordet må være over 8 tegn

Konklusjon

I denne opplæringen lærte vi om å skape egendefinerte Angular validators for skjemaer i Angular. 

Validators er funksjoner som returnerer null eller et feilobjekt. I modelldrevne skjemaer må vi legge til validatoren til en FormControl / FormGroup-forekomst, og det er det. Prosedyren var litt mer kompleks i en mal-drevet form, fordi vi trengte å lage et direktiv på toppen av validatorfunksjonen. 

Hvis du er interessert i å fortsette å lære mer om JavaScript, husk å sjekke ut hva vi har i Envato Market.

Jeg håper at du har hatt glede av denne serien på Forms in Angular. Jeg vil gjerne høre tankene dine. Del dem gjennom kommentarene.