Introduksjon til skjemaer i vinkel 4 mal-drevne skjemaer

Hva du skal skape

Skjemaer er kritiske for enhver moderne front-end-applikasjon, og de er en funksjon som vi bruker hver dag, selv om vi ikke skjønner det. Skjemaer kreves for å logge inn på en bruker til appen, og søke etter alle tilgjengelige hoteller i en bestemt by, bestille en drosje, bygge en gjøremålsliste og gjøre tonnevis med andre ting vi er vant til. Enkelte skjemaer har bare et par inngangsfelt, mens andre skjemaer kan ha en rekke felt som strekker seg til et par sider eller faner. 

I denne opplæringen snakker vi om ulike strategier som er tilgjengelige for å utvikle skjemaer i Angular. Uansett hvilken strategi du velger, er det de tingene som et skjemabibliotek bør dekke:

  • Støt toveisbinding slik at inngangskontrollverdiene synkroniseres med komponenttilstanden.
  • Hold oversikt over skjemaet tilstand og bruk visuelle signaler for å la brukeren vite om gjeldende tilstand er gyldig eller ikke. For eksempel, hvis brukernavnet har ugyldige tegn, skal det vises en rød ramme rundt inntastingsfeltet for brukernavnet.
  • Har en mekanisme for å vise valideringsfeil riktig.
  • Aktiver eller deaktiver visse deler av skjemaet, med mindre noen valideringskriterier er oppfylt.

Introduksjon til skjemaer i kantet

Angular, som er en fullverdig front-end rammeverk, har sitt eget sett med biblioteker for å bygge komplekse former. Den nyeste versjonen av Angular har to kraftige formbyggingsstrategier. De er:

  • mal-drevne former 
  • modell-drevne eller reaktive former

Begge teknologiene tilhører @ vinkel / former bibliotek og er basert på samme form kontroll klasser. Men de adskiller seg bemerkelsesverdig i sin filosofi, programmeringsstil og teknikk. Å velge den ene over den andre, avhenger av din personlige smak og også på kompleksiteten til skjemaet du prøver å skape. Etter min mening bør du prøve begge tilnærmingene først og velg en som passer til din stil og prosjektet ved hånden. 

Den første delen av opplæringen vil dekke mal-drevne skjemaer med et praktisk eksempel: å bygge et registreringsskjema med validering for alle skjemafelter. I den andre delen av denne opplæringen vil vi gå tilbake til trinnene for å lage det samme skjemaet ved hjelp av en modelldrevet tilnærming i stedet. 

Template-Driven Forms

Den mal-drevne tilnærmingen er en strategi som ble lånt fra AngularJS-epoken. Etter min mening er det den enkleste metoden for å bygge skjemaer. Hvordan virker det? Vi vil bruke noen kantede direktiver. 

Direktivene lar deg legge til atferd til elementer i DOM.
- Angular Documentation

Angular gir formsspesifikke direktiver som du kan bruke til å binde skjemainngangsdata og modellen. De formsspesifikke direktivene legger til ekstra funksjonalitet og oppførsel til en vanlig HTML-skjema. Sluttresultatet er at malen tar seg av bindende verdier med modell og form validering. 

I denne opplæringen bruker vi mal-drevne skjemaer for å opprette registreringssiden til et program. Skjemaet vil dekke de vanligste skjemaelementene og forskjellige valideringskontroller på disse skjemaelementene. Her er trinnene du vil følge i denne opplæringen.

  • Legg til FormsModule til app.module.ts.
  • Opprett en klasse for brukermodellen.
  • Opprett innledende komponenter og layout for påmeldingsskjemaet.
  • Bruk Angular form directives like ngModelngModelGroup, og ngForm.
  • Legg til validering ved hjelp av innebygde validatorer.
  • Vis validering feil meningsfylt.
  • Håndter skjema innsending ved hjelp av ngSubmit.

La oss komme i gang.

Forutsetninger

Koden for dette prosjektet er tilgjengelig på min GitHub repo. Last ned postnummeret eller klon repoen for å se det i aksjon. Hvis du foretrekker å starte fra scratch i stedet, må du sørge for at du har installert Angular CLI. Bruke ng kommandoen for å generere et nytt prosjekt. 

$ ng ny SignupFormProject

Deretter genererer du en ny komponent for SignupForm.

ng generere komponent SignupForm

Erstatt innholdet i app.component.html med dette:

 

Her er katalogstrukturen for src / katalogen. Jeg har fjernet noen ikke-essensielle filer for å holde ting enkelt.

. ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── signup-form │ │ ├ ─ - 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 

Som du kan se, en katalog for SignupForm komponenten er opprettet automatisk. Det er der det meste av koden vår vil gå. Jeg har også laget en ny User.ts for lagring av brukermodellen vår.

HTML-malen

Før vi dykker inn i selve komponentmalen, må vi ha en abstrakt ide om hva vi bygger. Så her er formstrukturen som jeg har i tankene mine. Påmeldingsskjemaet vil ha flere inntastingsfelter, et valgelement og et avkrysningselement. 


Her er HTML-malen som vi skal bruke til vår registreringsside. 

HTML-mal

 
Melde deg på

CSS-klassene som brukes i HTML-malen, er en del av Bootstrap-biblioteket som brukes til å gjøre ting pent. Siden dette er ikke en designopplæring, snakker jeg ikke mye om CSS-aspektene av skjemaet med mindre det er nødvendig. 

Grunnleggende formoppsett

For å kunne bruke formulardirektiver, må vi importere FormsModule fra @ vinkel / former og legg det til import array inn app.module.ts.

app / app.module.ts

importer FormModule fra '@ vinkler / skjemaer'; @NgModule (... import: [BrowserModule, FormsModule], ...) eksportklasse AppModule  

Deretter oppretter du en klasse som vil holde alle egenskapene til brukerenheten. Vi kan enten bruke et grensesnitt og implementere det i komponenten eller bruke en TypeScript-klasse for modellen.

app / User.ts

Eksportklasse Bruker id: nummer; e-post: streng; // Begge passordene er i et enkelt objektpassord: pwd: string; confirmPwd: string; ; kjønn: streng; vilkår: boolsk; konstruktør (verdier: Objekt = ) // Konstruktørinitialisering Object.assign (dette, verdier);  

Lag nå en forekomst av klassen i SignupForm-komponenten. Jeg har også erklært en ekstra eiendom for kjønn. 

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

importer Komponent, OnInit fra '@ vinkel / kjerne'; // Importer brukermodellimporten Bruker fra './.../User'; @Component (selector: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css']) eksportklasse SignupFormComponent implementerer OnInit // Egenskap for kjønn privat kjønn: streng []; // Egenskap for brukerens private bruker: Bruker; ngOnInit () this.gender = ['Male', 'Female', 'Others']; // Opprett et nytt brukerobjekt this.user = ny bruker (email: "), passord: pwd:" ", confirm_pwd:" ", kjønn: this.gender [0], vilkår: false);  

For registrering-form.component.html fil, jeg skal bruke den samme HTML-malen diskutert ovenfor, men med mindre endringer. Påmeldingsskjemaet har et velgfelt med en liste over alternativer. Selv om det fungerer, vil vi gjøre det på den kantede måten ved å løpe gjennom listen ved hjelp av ngFor direktiv.

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

Melde deg på...
...

Deretter ønsker vi å binde formdataene til brukerklasseobjektet slik at når du legger inn registreringsdataene i skjemaet, opprettes et nytt brukerobjekt som midlertidig lagrer dataene. På denne måten kan du holde synspunktet synkronisert med modellen, og dette kalles bindende. 

Det er et par måter å få dette til å skje. La meg først introdusere deg til ngModel og ngForm.

ngForm og ngModel

ngForm og ngModel er kantede direktiver som er essensielle for å lage mal-drevne skjemaer. La oss begynne med ngForm først. Her er et utdrag om ngForm fra Angular docs.

De NgForm Direktivet kompletterer skjema element med tilleggsfunksjoner. Den inneholder kontrollene du opprettet for elementene med en ngModel direktiv og Navn attributt, og overvåker deres egenskaper, inkludert deres gyldighet. Den har også sin egen gyldig eiendom som er sant bare hvis hver inneholdt kontroll er gyldig.

Først oppdaterer du skjemaet med ngForm direktiv:

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

...

#signupForm er en referansevariabel for mal som refererer til ngForm direktiv som styrer hele skjemaet. Eksempelet nedenfor viser bruken av a ngForm referanseobjekt for validering.

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

Her, signupForm.form.valid vil returnere falsk med mindre alle formelementene overfører sine respektive valideringskontroller. Send-knappen vil bli deaktivert til skjemaet er gyldig.  

Når det gjelder bindingen av malen og modellen, er det mange måter å gjøre dette på, og ngModel har tre forskjellige syntakser for å takle denne situasjonen. De er:

  1. [(NgModel)] 
  2. [NgModel]
  3. ngModel

La oss starte med den første.

Toveisbinding ved hjelp av [(ngModel)]

[(NgModel)] Utfører toveisbinding for å lese og skrive inngangskontrollverdier. Hvis en [(NgModel)] Direktivet brukes, inputfeltet tar en startverdi fra den bundne komponentklassen og oppdaterer den tilbake når noen endring av inngangskontrollverdien oppdages (ved tastetrykk og knappetrykk). Bildet nedenfor beskriver toveis bindingsprosessen bedre.

Her er koden for e-post-feltet:

 

[(ngModel)] = "user.email" binder brukerens e-postegenskap til inngangsverdien. Jeg har også lagt til en Navn attributt og sett name = "email". Dette er viktig, og du vil få en feil hvis du ikke har oppgitt et navnattributt mens du bruker ngModel. 

Tilsvarende legg til en [(NgModel)] og en unik Navn tilordne hvert formelement. Skjemaet ditt skal se slik ut nå:

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

... 
...

De ngModelGroup brukes til å gruppere sammen liknende skjemafelter, slik at vi bare kan kjøre valideringer på disse skjemafeltene. Siden begge passordfeltene er relaterte, vil vi sette dem under en enkelt ngModelGroup. Hvis alt fungerer som forventet, er komponentbundet bruker Eiendom skal være ansvarlig for å lagre alle formkontrollverdiene. For å se dette i aksjon, legg til følgende etter skjemaetiketten:

user | json

Rør brukeregenskapen gjennom JsonPipe å gjengi modellen som JSON i nettleseren. Dette er nyttig for feilsøking og logging. Du bør se en JSON-utgang som denne. 

Verdiene flyter inn fra visningen til modellen. Hva med den andre veien? Prøv å initialisere brukerobjektet med noen verdier.

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

this.user = ny bruker (// initialisert med noen data e-post: "[email protected]", passord: pwd: "", confirm_pwd: "", kjønn: this.gender [0]);

Og de vises automatisk i visningen:

"email": "[email protected]", "passord": "pwd": "", "confirm_pwd": "", "kjønn": "Mann"

Den toveisbindende [(NgModel)] syntaks hjelper deg å bygge skjemaer uten problemer. Det har imidlertid visse ulemper; Derfor er det en alternativ tilnærming som bruker ngModel eller [NgModel].

Legge til ngModel til Mix

Når ngModel brukes, er vi faktisk ansvarlige for å oppdatere komponentegenskapen med inngangskontrollverdiene og omvendt. Inngangsdata flyter ikke automatisk inn i komponentens brukeregenskap.

Så erstatt alle forekomster av [(ngModel)] = "" med ngModel. Vi vil beholde Navn attributt fordi alle tre versjonene av ngModel trenger Navn attributt til arbeid. 

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

Med ngModel, verdien av navnetattributtet blir en nøkkel til ngForm-referanseobjektet signupForm som vi opprettet tidligere. Så, for eksempel, signupForm.value.email vil lagre kontrollverdien for e-post-ID. 

Erstatte user | json med signupForm.value | json fordi det er der all staten er lagret akkurat nå. 

One-Way Binding ved hjelp av [ngModel]

Hva om du må sette innledende tilstand fra den bundne klassekomponenten? Det er det som [NgModel] gjør for deg. 

Her strømmer dataene fra modellen til visningen. Gjør følgende endringer i syntaksen for å bruke enveisbinding:

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

Så hvilken tilnærming bør du velge? Hvis du bruker [(NgModel)] og ngForm sammen, vil du til slutt ha to stater å opprettholde-bruker og signupForm.value-og det kan være potensielt forvirrende. 

"email": "[email protected]", "passord": "pwd": "thisispassword", "confirm_pwd": "thisispassword", "gender": "Male" //user.value " e-post ":" [email protected] "," passord ": " pwd ":" thisispassword "," confirm_pwd ":" thisispassword "," gender ":" Male " //signupForm.value 

Derfor vil jeg anbefale å bruke enveisbindingsmetoden i stedet. Men det er noe for deg å bestemme.

Validering og visning av feilmeldinger 

Her er våre krav til validering.

  • Alle skjema kontroller er nødvendig.
  • Deaktiver innsendingsknappen til alle feltene er fylt ut.
  • E-postfeltet bør strengt inneholde et e-post-ID.
  • Passordfeltet skal ha en minimumslengde på 8.
  • Både passordet og bekreftelsen skal samsvare.
Vårt skjema med validering på plass

Den første er lett. Du må legge til en nødvendig valideringsattributt til hvert formelement som dette:

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

Bortsett fra nødvendig attributt, jeg har også eksportert en ny #email Temperatur referansevariabel. Dette er slik at du kan få tilgang til inngangsruten Angular-kontroll fra selve malen. Vi vil bruke den til å vise feil og advarsler. Bruk nå knappens deaktiverte egenskap for å deaktivere knappen:

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

For å legge til en begrensning på e-post, bruk mønsterattributtet som fungerer med inntastingsfelter. Mønstre brukes til å angi regulære uttrykk som den nedenfor:

Mønsteret = "[a-Z0-9 ._% + -]. + @ [a-Z0-9 .-] + \ [a-z] 2,3 $"

For passordfeltet er alt du trenger å gjøre å legge til en minlength = "" Egenskap:

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

 

For å vise feilene, skal jeg bruke betingelsesdirektivet ngIf på et div-element. La oss starte med inngangskontrollfeltet for e-post:

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

E-postfeltet kan ikke være tomt
E-post-ID-en virker ikke riktig

Det skjer mye her. La oss starte med den første linjen i feilseksjonen.

Husk #email variabel som vi eksporterte tidligere? Den har litt informasjon om inngangskontrolltilstanden til e-postfeltet. Dette inkluderer: email.valid, email.invalid, email.dirty, email.pristine, email.touched, email.untouched, og email.errors.  Bildet nedenfor beskriver hver av disse egenskapene i detalj.

Så div elementet med * ngIf vil bli gjengitt bare hvis e-postadressen er ugyldig. Brukeren vil imidlertid bli møtt med feil om at inntastingsfeltene er tomme selv før de har mulighet til å redigere skjemaet. 

For å unngå dette scenarioet har vi lagt til den andre betingelsen. Feilen vil bli vist bare etter kontrollen har blitt besøkt eller kontrollens verdi er endret.

De nestede div-elementene brukes til å dekke alle tilfeller av valideringsfeil. Vi bruker email.errors for å sjekke alle mulige valideringsfeil og deretter vise dem tilbake til brukeren i form av egendefinerte meldinger. Følg nå samme fremgangsmåte for de andre skjemaelementene. Slik har jeg kodet valideringen for passordene. 

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

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

Dette begynner å se litt rotete ut. Angular har et begrenset sett av validatorattributter: nødvendig, minlength, maks lengde, og mønster. For å dekke et annet scenario som for å sammenligne passord, må du stole på nestet ngIf conditionals som jeg gjorde ovenfor. Eller ideell, opprett en egendefinert valideringsfunksjon, som jeg vil dekke i tredje del av denne serien.

I koden ovenfor har jeg brukt ngIf annet syntaks som ble introdusert i den siste versjonen av Angular. Slik fungerer det:

Gyldig innhold ...
Ikke gyldig innhold ...

Send inn skjemaet ved hjelp av ngSubmit

Vi har nesten fullført skjemaet. Nå må vi kunne sende inn skjemaet, og kontrollen av skjemadataene skal overlevert til en komponentmetode, sier onFormSubmit ().

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

...

Nå, for komponenten:

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

... offentlig onFormSubmit (verdi, gyldig: verdi: Bruker, gyldig: boolean) this.user = value; console.log (this.user); console.log ("gyldig:" + gyldig);  ... 

Endelig demonstrasjon

Her er den endelige versjonen av søknaden. Jeg har lagt til noen få bootstrap-klasser for å gjøre skjemaet pent.

Sammendrag

Vi er alle ferdige her. I denne opplæringen dekket vi alt du trenger å vite om å lage et skjema i Angular ved hjelp av mal-drevet tilnærming. Mal-drevne skjemaer er populære for enkelhet og brukervennlighet. 

Men hvis du trenger å bygge et skjema med mange formelementer, vil denne tilnærmingen bli rotete. Så i neste opplæring vil vi dekke den modelldrevne måten å bygge samme skjema på. 

Del dine tanker i kommentarene nedenfor.

Lær JavaScript: Den komplette veiledningen

Vi har bygget en komplett guide for å hjelpe deg med å lære JavaScript, enten du er bare i gang som webutvikler eller du vil utforske mer avanserte emner.