Introduktion till formulär i vinkel 4 Malldriven form

Vad du ska skapa

Blanketter är kritiska för alla moderna applikationer, och de är en funktion som vi använder varje dag, även om vi inte inser det. Blanketter krävs för att logga in säkert på en användare till appen, leta efter alla tillgängliga hotell i en viss stad, boka en hytt, bygga en att göra-lista och göra massor av andra saker som vi är vana vid. Vissa former har bara några inmatningsfält, medan andra former kan ha en rad fält som sträcker sig till ett par sidor eller flikar. 

I denna handledning kommer vi att prata om olika strategier som är tillgängliga för att utveckla former i Angular. Oavsett vilken strategi du väljer, här är de saker som ett formulärbibliotek bör täcka:

  • Stöd tvåvägsbindning så att ingångskontrollvärdena synkroniseras med komponenttillståndet.
  • Håll reda på formuläret och använd visuella signaler för att låta användaren veta om det aktuella läget är giltigt eller inte. Om användarnamnet till exempel har ogiltiga tecken, ska en röd gräns visas runt inmatningsfältet för användarnamnet.
  • Ha en mekanism för att visa valideringsfel korrekt.
  • Aktivera eller inaktivera vissa delar av formuläret om inte några valideringskriterier är uppfyllda.

Introduktion till former i vinkel

Angular, som är en fullfjädrad fronten-ram, har sin egen uppsättning bibliotek för att bygga komplexa former. Den senaste versionen av Angular har två kraftfulla formbyggnadsstrategier. Dom är:

  • mallstyrda former 
  • modelldrivna eller reaktiva former

Båda teknologierna hör till @ kantiga / former biblioteket och bygger på samma formkontrollklasser. Men de skiljer sig anmärkningsvärt i sin filosofi, programmeringsstil och teknik. Att välja den ena över den andra beror på din personliga smak och på komplexiteten i den form som du försöker skapa. Enligt min mening borde du prova båda metoderna först och välj sedan en som passar din stil och projektet för hand. 

Den första delen av handledningen kommer att omfatta mallstyrda blanketter med ett praktiskt exempel: Skapa en anmälningsblankett med validering för alla formulärfält. I den andra delen av denna handledning återfår vi stegen för att skapa samma form med hjälp av en modelldriven inriktning istället. 

Template-Driven Forms

Den mallstyrda strategin är en strategi som lånades från AngularJS-tiden. Enligt min mening är det den enklaste metoden för att bygga former. Hur fungerar det? Vi kommer att använda några Angular Directives. 

Direktiv ger dig möjlighet att bifoga beteende till element i DOM.
- Angular Documentation

Angular ger formulärspecifika direktiv som du kan använda för att binda formulärinmatningsdata och modell. De formspecifika direktiven lägger till extra funktionalitet och beteende till en vanlig HTML-form. Slutresultatet är att mallen tar hand om bindande värden med modellen och formvalideringen. 

I den här handledningen använder vi mallstyrda formulär för att skapa anmälningssidan för en applikation. Formuläret kommer att täcka de vanligaste formulärelementen och olika valideringskontroller av dessa formulärelement. Här är de steg du följer i denna handledning.

  • Lägg till FormsModule till app.module.ts.
  • Skapa en klass för användarmodellen.
  • Skapa initiala komponenter och layout för registreringsformuläret.
  • Använd Angular Form Directives som ngModelngModelGroup, och ngForm.
  • Lägg till validering med inbyggda validatorer.
  • Visa valideringsfel meningsfullt.
  • Hantera formulärinsändning med ngSubmit.

Låt oss börja.

förutsättningar

Koden för detta projekt finns på min GitHub repo. Ladda ner zip eller klon repo för att se den i åtgärd. Om du föredrar att börja från början istället, se till att du har installerat Angular CLI. Använd ng kommando för att skapa ett nytt projekt. 

$ ng nytt SignupFormProject

Skapa sedan en ny komponent för SignupForm.

ng generera komponent SignupForm

Ersätt innehållet i app.component.html med detta:

 

Här är katalogstrukturen för src / katalogen. Jag har tagit bort några icke-nödvändiga filer för att hålla sakerna enkla.

. ├── app │ ├── app.component.css │ ├── app.component.html │ ├── app.component.ts │ ├── app.module.ts │ ├── registreringsformulär │ │ ├ ─ - 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 för SignupForm komponenten har skapats automatiskt. Det är där de flesta av vår kod kommer att gå. Jag har också skapat en ny User.ts för att lagra vår användarmodell.

HTML-mallen

Innan vi dyker in i själva komponentmallen behöver vi ha en abstrakt idé om vad vi bygger. Så här är den formstruktur som jag har i mitt sinne. Anmälningsformuläret kommer att ha flera inmatningsfält, ett väljelement och en kryssruta. 


Här är HTML-mallen som vi ska använda för vår registreringssida. 

HTML-mall

 
Bli Medlem

CSS-klasserna som används i HTML-mallen är en del av Bootstrap-biblioteket som används för att göra saker ganska. Eftersom detta inte är en designtutorial, kommer jag inte att prata mycket om CSS-aspekterna av formuläret om det inte behövs. 

Grundläggande formulärinställning

För att kunna använda de malldrivna formulärdirektiven måste vi importera FormsModule från @ kantiga / former och lägg till den i import array in app.module.ts.

app / app.module.ts

importera FormsModule från '@ vinkel / formulär'; @NgModule (... import: [BrowserModule, FormsModule], ...) exportklass AppModule  

Skapa sedan en klass som håller alla egenskaper hos användar-enheten. Vi kan antingen använda ett gränssnitt och implementera det i komponenten eller använda en TypeScript-klass för modellen.

app / User.ts

exportklass Användare id: nummer; email: string; // Båda lösenorden finns i ett enda objektlösenord: pwd: string; confirmPwd: string; ; kön: sträng; termer: booleska; konstruktör (värden: Objekt = ) // Konstruktorinitialisering Object.assign (detta, värden);  

Skapa nu en förekomst av klassen i SignupForm-komponenten. Jag har också deklarerat en extra egenskap för könet. 

app / registrerings-formen / signup-form.component.ts

importera Component, OnInit från '@ vinkel / kärna'; // Importera användarnamporten Användare från './.../User'; @Component (selector: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css']) exportklass SignupFormComponent implementerar OnInit // Egenskap för kön privat kön: sträng []; // Egenskap för användarens privata användare: Användare; ngOnInit () this.gender = ['Male', 'Female', 'Others']; // Skapa ett nytt användarobjekt this.user = new User (email: "), lösenord: pwd:" ", confirm_pwd:" ", kön: this.gender [0], termer: false);  

För registrering-form.component.html fil, kommer jag att använda samma HTML-mall som diskuterats ovan, men med mindre ändringar. Anmälningsformuläret har ett väljfält med en lista med alternativ. Även om det fungerar, kommer vi att göra det på vinkeln genom att slingra igenom listan med hjälp av ngFor direktiv.

app / registrerings-formen / registrering-form.component.html

Bli Medlem...
...

Därefter vill vi binda formulärdata till användarklassobjektet så att när du anger registreringsdata i formuläret skapas ett nytt användarobjekt som tillfälligt lagrar den data. På så vis kan du hålla synvinkel synkroniserad med modellen, och detta kallas bindande. 

Det finns ett par sätt att få detta att hända. Låt mig först presentera dig för ngModel och ngForm.

ngForm och ngModel

ngForm och ngModel är vinkeldirektiv som är nödvändiga för att skapa mallstyrda former. Låt oss börja med ngForm först. Här är ett utdrag om ngForm från Angular docs.

De NgForm direktivet kompletterar form element med ytterligare funktioner. Den innehåller kontrollerna du skapade för elementen med en ngModel direktiv och namn attribut och övervakar deras egenskaper, inklusive deras giltighet. Det har också sin egen giltig egendom som bara är sant om alla innehöll kontrollen är giltig.

Först uppdatera formuläret med ngForm direktiv:

app / registrerings-formen / registrering-form.component.html

...

#signupForm är en referensvariabel för mall som refererar till ngForm direktiv som styr hela formuläret. Exemplet nedan visar användningen av a ngForm referensobjekt för validering.

app / registrerings-formen / registrering-form.component.html

Här, signupForm.form.valid kommer att returneras falskt om inte alla formulärelement överensstämmer med deras respektive valideringskontroller. Ingivningsknappen kommer att inaktiveras tills formuläret är giltigt.  

När det gäller bindning av mallen och modellen finns det många sätt att göra detta, och ngModel har tre olika syntaxer för att hantera denna situation. Dom är:

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

Låt oss börja med den första.

Tvåvägsbindning med hjälp av [(ngModel)]

[(NgModel)] utför dubbelriktad bindning för läsning och skrivning av inmatningskontrollvärden. Om en [(NgModel)] direktivet används, matar inmatningsfältet ett initialvärde från den bundna komponentklassen och uppdaterar den tillbaka när någon ändring av ingångskontrollvärdet detekteras (vid tangenttryckning och knapptryckning). Bilden nedan beskriver den tvåvägsbindande processen bättre.

Här är koden för inmatningsfältet för e-post:

 

[(ngModel)] = "user.email" binder användarens e-postegenskap till inmatningsvärdet. Jag har också lagt till en namn attribut och set name = "email". Detta är viktigt, och du kommer att få ett fel om du inte har deklarerat ett namnattribut medan du använder ngModel. 

På samma sätt lägg till en [(NgModel)] och en unik namn attribut till varje formelement. Din form ska se något så här nu:

app / registrerings-formen / registrering-form.component.html

... 
...

De ngModelGroup används för att gruppera liknande formulärfält så att vi kan köra valideringar endast i dessa formulärfält. Eftersom båda lösenordsfälten är relaterade lägger vi dem under en enda ngModelGroup. Om allt fungerar som förväntat, komponentbundet användare egendom bör ansvara för att lagra alla formkontrollvärden. För att se detta i åtgärd, lägg till följande efter formtaggen:

user | json

Rör användaregenskapen genom JsonPipe att göra modellen som JSON i webbläsaren. Det här är användbart för felsökning och loggning. Du bör se en JSON-utgång så här. 

Värdena strömmar in från visningen till modellen. Vad sägs om tvärtom? Försök initiera användarobjektet med vissa värden.

app / registrerings-formen / signup-form.component.ts

this.user = ny användare (// initialiserad med viss data email: "[email protected]", lösenord: pwd: "", confirm_pwd: "", kön: this.gender [0]);

Och de visas automatiskt i vyn:

"email": "[email protected]", "lösenord": "pwd": "", "confirm_pwd": "", "gender": "Male"

Tvåvägsbindningen [(NgModel)] syntax hjälper dig att bygga formulär utan problem. Det har emellertid vissa nackdelar; Därför finns det ett alternativt tillvägagångssätt som använder ngModel eller [NgModel].

Lägger till ngModel i mixen

När ngModel används, är vi faktiskt ansvariga för att uppdatera komponentegenskapen med inmatningskontrollvärdena och vice versa. Inmatningsdata flyter inte automatiskt till komponentens användaregenskap.

Så ersätt alla instanser av [(ngModel)] = "" med ngModel. Vi kommer att behålla namn attribut eftersom alla tre versioner av ngModel behöver namn attribut till arbetet. 

app / registrerings-formen / registrering-form.component.html

Med ngModel, värdet på namnattributet blir en nyckel i ngForm-referensobjektet signupForm som vi skapade tidigare. Så, till exempel, signupForm.value.email lagrar kontrollvärdet för e-post-id. 

Byta ut user | json med signupForm.value | json för det är där all staten lagras just nu. 

One-way-bindning med hjälp av [ngModel]

Vad händer om du behöver ställa in det ursprungliga tillståndet från den bundna klasskomponenten? Det är vad [NgModel] gör för dig. 

Här strömmar data från modellen till vyn. Gör följande ändringar i syntaxen för att använda envägsbindning:

app / registrerings-formen / registrering-form.component.html

Så vilken strategi ska du välja? Om du använder [(NgModel)] och ngForm tillsammans kommer du så småningom att ha två stater att behålla-användare och signupForm.value-och det kan vara potentiellt förvirrande. 

"email": "[email protected]", "lösenord": "pwd": "thisispassword", "confirm_pwd": "thisispassword", "gender": "Male" //user.value " email ":" [email protected] "," lösenord ": " pwd ":" thisispassword "," confirm_pwd ":" thisispassword "," gender ":" Male " //signupForm.value 

Därför rekommenderar jag att du använder den envägs bindande metoden istället. Men det är något för dig att bestämma.

Validering och visning av felmeddelanden 

Här är våra krav på validering.

  • Alla formulär kontroller är nödvändig.
  • Inaktivera inmatningsknappen tills alla inmatningsfält är fyllda.
  • E-postfältet bör strikt innehålla ett e-post-ID.
  • Lösenordsfältet ska ha en minsta längd av 8.
  • Både lösenordet och bekräftelsen ska matcha.
Vår form med validering på plats

Den första är lätt. Du måste lägga till en nödvändig validering attribut till varje formulär element så här:

app / registrerings-formen / registrering-form.component.html

Förutom nödvändig attribut, jag har också exporterat en ny #e-post mallreferensvariabel. Det här är så att du kan komma åt inmatningsrutans kantlinjekontroll från mallen själv. Vi använder den för att visa fel och varningar. Använd nu knappens inaktiverade egenskap för att inaktivera knappen:

app / registrerings-formen / registrering-form.component.html

För att lägga till en begränsning på e-post, använd mönsterattributet som fungerar med inmatningsfält. Mönster används för att specificera reguljära uttryck som nedan:

mönster = "[a-z0-9 ._% + -]. + @ [a-z0-9 .-] + \ [a-z] 2,3 $"

För lösenordet är allt du behöver göra till att lägga till en minlength = "" attribut:

app / registrerings-formen / registrering-form.component.html

 

För att visa felen kommer jag att använda det villkorliga direktivet ngIf på ett div-element. Låt oss börja med ingångskontrollfältet för e-post:

app / registrerings-formen / registrering-form.component.html

E-postfältet kan inte vara tomt
E-post-id verkar inte rätt

Det händer mycket här. Låt oss börja med den första raden i felsektionen.

Kom ihåg det #e-post variabel som vi exporterade tidigare? Den har viss information om ingångskontrolltillståndet i e-postfältet. Detta inkluderar: email.valid, email.invalid, email.dirty, email.pristine, email.touched, email.untouched, och email.errors.  Bilden nedan beskriver i detalj alla dessa egenskaper.

Så div elementet med * ngIf kommer endast att göras om e-postmeddelandet är ogiltigt. Användaren kommer emellertid att hälsas med fel om inmatningsfälten är tomma även innan de har möjlighet att redigera formuläret. 

För att undvika detta scenario har vi lagt till det andra villkoret. Felet visas först efter kontrollen har besökts eller kontrollens värde har ändrats.

De kapslade div-elementen används för att täcka alla fall av valideringsfel. Vi använder email.errors för att kontrollera alla möjliga valideringsfel och sedan visa dem tillbaka till användaren i form av anpassade meddelanden. Följ nu samma procedur för övriga formulärelement. Så här har jag kodat validering för lösenord. 

app / registrerings-formen / registrering-form.component.html

 
Lösenordet måste vara mer än 8 tecken
Lösenorden matchar inte

Detta börjar se lite rörigt ut. Angular har en begränsad uppsättning validatorattribut: nödvändig, minlength, Maxlängd, och mönster. För att täcka alla andra scenarier som lösenordsjämförelse måste du lita på kapslade ngIf conditionals som jag gjorde ovan. Eller helst skapar du en anpassad validatorfunktion som jag kommer att täcka i den tredje delen av denna serie.

I koden ovan har jag använt ngIf annat syntax som introducerades i den senaste versionen av Angular. Så här fungerar det:

Giltigt innehåll ...
Ej giltigt innehåll ...

Skicka formuläret med ngSubmit

Vi har nästan slutfört formuläret. Nu måste vi kunna lämna in formuläret och kontrollen av formulärdata ska överlämnas till en komponentmetod, säg onFormSubmit ().

app / registrerings-formen / signup-form.component.ts

...

Nu för komponenten:

app / registrerings-formen / signup-form.component.ts

... public onFormSubmit (värde, giltigt: värde: User, valid: boolean) this.user = value; console.log (this.user); console.log ("valid:" + valid);  ... 

Final Demo

Här är den slutliga versionen av ansökan. Jag har lagt till några bootstrap klasser för att göra formuläret ganska.

Sammanfattning

Vi är alla gjorda här. I den här handledningen täckte vi allt som du behöver veta om att skapa en form i Angular med hjälp av den mallstyrda metoden. Malldrivna former är populära för sin enkelhet och användarvänlighet. 

Men om du behöver bygga ett formulär med massor av formelement kommer det här sättet att bli rörigt. Så, i nästa handledning kommer vi att täcka det modellstyrda sättet att bygga samma form. 

Dela dina tankar i kommentarerna nedan.

Lär dig JavaScript: Den fullständiga guiden

Vi har byggt en komplett guide för att hjälpa dig att lära dig JavaScript, oavsett om du precis börjat som webbutvecklare eller vill utforska mer avancerade ämnen.