Einführung in die Formen in Winkel 4 Reaktive Formen

Was Sie erstellen werden

Dies ist der zweite Teil der Serie "Einführung in Formulare in Winkel 4". Im ersten Teil haben wir ein Formular mit dem vorlagengesteuerten Ansatz erstellt. Wir verwendeten Richtlinien wie ngModel, ngModelGroup und ngForm um die Formularelemente zu überladen. In diesem Lernprogramm werden wir einen anderen Ansatz zum Erstellen von Formen verwenden - der reaktive Weg. 

Reaktive Formen

Reaktive Formulare haben einen anderen Ansatz als die von Vorlagen angetriebenen Formulare. Hier erstellen und initialisieren wir die Kontrollobjekte bilden in unserer Komponentenklasse. Sie sind Zwischenobjekte, die den Zustand des Formulars enthalten. Wir werden sie dann an das binden Steuerelemente bilden in der Vorlage.

Das Formularsteuerungsobjekt überwacht jede Änderung der Eingabesteuerungswerte und wird sofort im Status des Objekts angezeigt. Da die Komponente direkten Zugriff auf die Datenmodellstruktur hat, können alle Änderungen zwischen dem Datenmodell, dem Formularsteuerungsobjekt und den Eingabesteuerungswerten synchronisiert werden. 

Wenn wir ein Formular zum Aktualisieren des Benutzerprofils erstellen, ist das Datenmodell praktisch das vom Server abgerufene Benutzerobjekt. Normalerweise wird dies häufig in der Benutzereigenschaft der Komponente gespeichert (this.user). Das Formularsteuerungsobjekt oder das Formularmodell wird an die eigentlichen Formularsteuerelemente der Vorlage gebunden.

Beide Modelle sollten ähnliche Strukturen haben, auch wenn sie nicht identisch sind. Die Eingabewerte sollten jedoch nicht direkt in das Datenmodell einfließen. Das Bild beschreibt, wie die Benutzereingaben aus der Vorlage in das Formularmodell gelangen.

Lass uns anfangen.

Voraussetzungen

Sie müssen Teil 1 dieser Serie nicht befolgt haben, damit Teil 2 einen Sinn ergibt. Wenn Sie jedoch noch nicht mit Angular-Formularen vertraut sind, würde ich dringend empfehlen, die auf Vorlagen basierende Strategie durchzugehen. Der Code für dieses Projekt ist in meinem GitHub-Repository verfügbar. Stellen Sie sicher, dass Sie sich im richtigen Zweig befinden, und laden Sie dann die ZIP-Datei herunter. Alternativ können Sie auch das Repo klonen, um das Formular in Aktion zu sehen. 

Wenn Sie lieber von vorne anfangen möchten, vergewissern Sie sich, dass Angular CLI installiert ist. Verwenden Sie die ng Befehl zum Erzeugen eines neuen Projekts. 

$ ng neues SignupFormProject

Generieren Sie anschließend eine neue Komponente für die Anmeldeformular oder manuell erstellen. 

ng generiert die Komponente SignupForm

Ersetzen Sie den Inhalt von app.component.html mit diesem:

 

Hier ist die Verzeichnisstruktur für die src / Verzeichnis. Ich habe einige nicht wesentliche Dateien entfernt, um die Dinge einfach zu halten.

. ├── app │ app.component.css app.component.html ├── ├── app.component.ts ts. App.module.ts ├── ├── Anmeldeformular-├── ├ ── signup-form.component.css │ up up up up signup-form.component.html │ └── up up up up up up up up up up up... .ts ├── polyfills.ts ├── styles.css ├── tsconfig.app.json └── typings.d.ts 

Wie Sie sehen, ein Verzeichnis für die Anmeldeformular Komponente wurde automatisch erstellt. Das ist, wo der größte Teil unseres Codes gehen wird. Ich habe auch ein neues erstellt User.ts zur Speicherung unseres Benutzermodells.

Die HTML-Vorlage

Bevor wir in die eigentliche Komponentenvorlage eintauchen, müssen wir eine abstrakte Vorstellung davon haben, was wir bauen. Hier ist also die Formstruktur, die ich im Kopf habe. Das Anmeldeformular enthält mehrere Eingabefelder, ein Auswahlelement und ein Kontrollkästchenelement. 


Hier ist die HTML-Vorlage, die wir für unsere Registrierungsseite verwenden werden. 

HTML-Vorlage

 
Anmelden

Die in der HTML-Vorlage verwendeten CSS-Klassen sind Teil der Bootstrap-Bibliothek, mit der Dinge schön gemacht werden. Da dies kein Design-Tutorial ist, werde ich nicht viel über die CSS-Aspekte des Formulars sprechen, sofern dies nicht erforderlich ist. 

Grundlegendes Formular-Setup

Um ein Reactive-Formular zu erstellen, müssen Sie das importieren ReactiveFormsModule von @ eckig / formen und fügen Sie es dem Import-Array in hinzu app.module.ts.

app / app.module.ts

// Importieren von ReactiveFormsModule Import ReactiveFormsModule aus '@ angle / forms'; @NgModule (… // Fügen Sie das Modul den Importen hinzu. Array-Importe: [BrowserModule, ReactiveFormsModule…) Exportklasse AppModule  

Als Nächstes erstellen Sie ein Benutzermodell für das Registrierungsformular. Wir können entweder eine Klasse oder eine Schnittstelle zum Erstellen des Modells verwenden. Für dieses Tutorial werde ich eine Klasse mit den folgenden Eigenschaften exportieren.

app / User.ts

Exportklasse Benutzer ID: Nummer; E-Mail: Zeichenfolge; // Beide Passwörter befinden sich in einem einzigen Objekt. Password: pwd: string; confirmPwd: Zeichenfolge; ; Geschlecht: String; Begriffe: boolean; Konstruktor (Werte: Object = ) // Konstruktorinitialisierung Object.assign (this, values);  

Erstellen Sie nun eine Instanz des Benutzermodells in der Anmeldeformular Komponente. 

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

import Component, OnInit aus '@ angle / core'; // Importieren Sie das Benutzermodell importieren User aus './… / User'; @Component (Selector: 'app-signup-form', templateUrl: './signup-form.component.html', styleUrls: ['./signup-form.component.css']) export-Klasse SignupFormComponent implementiert OnInit // Liste der Geschlechter für das ausgewählte Steuerelement private genderList: string []; // Eigenschaft für den privaten Benutzer des Benutzers: User; ngOnInit () this.genderList = ['männlich', 'weiblich', 'andere']; 

Für die Anmeldeformular.component.html Datei, ich werde die gleiche HTML-Vorlage verwenden, die oben beschrieben wurde, jedoch mit geringfügigen Änderungen. Das Anmeldeformular enthält ein Auswahlfeld mit einer Liste von Optionen. Obwohl dies funktioniert, machen wir es auf die Angular-Weise, indem wir die Liste mit der Schleife durchlaufen ngFor Richtlinie.

app / signup-form / signup-form.component.html

Anmelden

Hinweis: Möglicherweise wird eine Fehlermeldung angezeigt Kein Anbieter für ControlContainer. Der Fehler wird angezeigt, wenn eine Komponente über eine

Tag ohne eine formGroup-Direktive. Der Fehler wird ausgeblendet, wenn wir später im Lernprogramm eine FormGroup-Direktive hinzufügen.

Wir haben eine Komponente, ein Modell und eine Formularvorlage zur Hand. Was jetzt? Es ist an der Zeit, uns die Hände schmutzig zu machen und sich mit den APIs vertraut zu machen, die Sie zum Erstellen reaktiver Formulare benötigen. Das beinhaltet FormControl und Gruppe formen

Verfolgung des Status mithilfe von FormControl

Beim Erstellen von Formularen mit der Strategie für reaktive Formulare werden Sie nicht auf die Direktiven ngModel und ngForm stoßen. Stattdessen verwenden wir die zugrunde liegende FormControl- und FormGroup-API.

Ein FormControl ist eine Anweisung, die zum Erstellen einer FormControl-Instanz verwendet wird, mit der Sie den Status eines bestimmten Formularelements und dessen Validierungsstatus verfolgen können. So funktioniert FormControl:

/ * FormControl zuerst importieren * / import FormControl aus '@ angle / forms'; / * Beispiel für das Erstellen einer neuen FormControl-Instanz * / export-Klasse SignupFormComponent email = new FormControl (); 

Email ist jetzt eine FormControl-Instanz, die Sie wie folgt an ein Eingabesteuerelement in Ihrer Vorlage binden können:

Anmelden

Das Vorlagenformularelement ist jetzt an die FormControl-Instanz in der Komponente gebunden. Dies bedeutet, dass jede Änderung des Eingabesteuerwerts am anderen Ende angezeigt wird. 

Ein FormControl-Konstruktor akzeptiert drei Argumente - einen Anfangswert, ein Array von Synchronisationsprüfern und ein Array von Asynchronprüfern - und, wie Sie vielleicht schon vermutet haben, sind sie alle optional. Wir werden hier die ersten beiden Argumente behandeln. 

Validators aus '@ angle / forms' importieren;… / * FormControl mit Anfangswert und Validator * / email = new FormControl ('[email protected] ', Validators.required); 

Angular verfügt über eine begrenzte Anzahl eingebauter Validatoren. Die gängigen Validator-Methoden umfassen Validators.required, Validators.minLength, Validators.maxlength, und Validators.pattern. Um sie zu verwenden, müssen Sie jedoch zunächst die Validator-API importieren.

Für unser Anmeldeformular haben wir mehrere Eingabesteuerungsfelder (für E-Mail und Passwort), ein Auswahlfeld und ein Kontrollkästchen. Anstatt individuell zu schaffen FormControl würde es nicht sinnvoller sein, all diese Objekte zu gruppieren FormControls unter einer einzigen Einheit? Dies ist vorteilhaft, da wir jetzt den Wert und die Gültigkeit aller Sub-FormControl-Objekte an einer Stelle verfolgen können. Das ist, was Gruppe formen ist für. Also registrieren wir eine übergeordnete FormGroup mit mehreren untergeordneten FormControls. 

Gruppieren Sie mehrere FormControls mit FormGroup

Um eine FormGroup hinzuzufügen, importieren Sie sie zuerst. Als nächstes deklarieren Sie signupForm als Klasseneigenschaft und initialisieren Sie es wie folgt:

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

// Importiere die API zum Erstellen eines Formularimports FormControl, FormGroup, Validators aus '@ angle / forms'; Exportklasse SignupFormComponent implementiert OnInit genderList: String []; signupForm: FormGroup;… ngOnInit () this.genderList = ['männlich', 'weiblich', 'andere']; this.signupForm = new FormGroup (email: new FormControl (", Validators.required), pwd: new FormControl (), confirmPwd: new FormControl (), gender: new FormControl (), Begriffe: new FormControl ()) 

Binden Sie das FormGroup-Modell wie folgt an das DOM: 

app / signup-form / signup-form.component.html

  
Anmelden

[formGroup] = "signupForm" teilt Angular mit, dass Sie dieses Formular mit dem verknüpfen möchten Gruppe formen in der Komponentenklasse deklariert. Wenn Angular sieht formControlName = "E-Mail", Es wird nach einer Instanz von FormControl mit dem Schlüsselwert gesucht Email innerhalb der übergeordneten FormGroup. 

Aktualisieren Sie auf ähnliche Weise die anderen Formularelemente, indem Sie a hinzufügen formControlName = "Wert" Attribut wie wir gerade hier gemacht haben.

Um zu sehen, ob alles wie erwartet funktioniert, fügen Sie nach dem Formular-Tag Folgendes hinzu:

app / signup-form / signup-form.component.html

 

Formularwert signupForm.value | Json

Formularstatus signupForm.status | Json

Pipe die Anmeldeformular Eigentum durch die JsonPipe um das Modell als JSON im Browser darzustellen. Dies ist hilfreich beim Debuggen und Protokollieren. Sie sollten eine solche JSON-Ausgabe sehen.

Hier sind zwei Dinge zu beachten:

  1. Der JSON stimmt nicht genau mit der Struktur des zuvor erstellten Benutzermodells überein. 
  2. Das signupForm.status Zeigt an, dass der Status des Formulars UNGÜLTIG ist. Dies zeigt deutlich, dass die Validators.required auf dem E-Mail-Kontrollfeld funktioniert wie erwartet. 

Die Struktur des Formularmodells und des Datenmodells sollten übereinstimmen. 

// Formularmodell "email": "", "pwd": "", "confirmPwd": "", "gender": "", "terms": false // Benutzermodell "email": "" , "password": "pwd": "", "confirmPwd": "",, "gender": "", "terms": false

Um die hierarchische Struktur des Datenmodells abzurufen, sollten wir eine verschachtelte FormGroup verwenden. Darüber hinaus ist es immer eine gute Idee, verwandte Formularelemente unter einer einzigen FormGroup zu haben. 

Geschachtelte FormGruppe

Erstellen Sie eine neue FormGroup für das Kennwort.

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

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

Nehmen Sie die folgenden Änderungen vor, um das neue Formularmodell an das DOM zu binden:

app / signup-form / signup-form.component.html

 

formGroupName = "Passwort" führt die Bindung für die verschachtelte FormGroup aus. Nun entspricht die Struktur des Formularmodells unseren Anforderungen.

Formularwert: "email": "", "password": "pwd": null, "confirmPwd": null, "gender": null, "terms": null Formularstatus "UNGÜLTIG"

Als nächstes müssen wir die Formularsteuerelemente überprüfen.

Validierung des Formulars

Wir haben eine einfache Validierung für die E-Mail-Eingabesteuerung. Das reicht jedoch nicht aus. Hier ist die gesamte Liste unserer Anforderungen für die Validierung.

  • Alle Formularsteuerelemente sind erforderlich.
  • Deaktivieren Sie die Schaltfläche "Senden", bis der Status des Formulars GÜLTIG ist.
  • Das E-Mail-Feld sollte ausschließlich eine E-Mail-ID enthalten.
  • Das Passwortfeld sollte eine Mindestlänge von 8 haben.

Der erste ist einfach. Hinzufügen Validator.erfordert an alle FormControls im Formularmodell. 

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

 this.signupForm = new FormGroup (email: new FormControl (", Validators.required), Kennwort: new FormGroup (pwd: new FormControl (", Validators.required), confirmPwd: new FormControl (", Validators.required) ), gender: new FormControl (", Validators.required), // requiredTrue, so dass das Feld" Terms "nur gültig ist, wenn markierte Bedingungen aktiviert sind: new FormControl (", Validators.requiredTrue))

Deaktivieren Sie anschließend die Schaltfläche, während das Formular UNGÜLTIG ist.

app / signup-form / signup-form.component.html

 

Um eine Einschränkung für E-Mails hinzuzufügen, können Sie entweder die Standardeinstellung verwenden Validators.email oder erstellen Sie eine benutzerdefinierte Validators.pattern () das gibt reguläre Ausdrücke wie den folgenden an:

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

Verwenden Sie die minimale Länge Validator für die Passwortfelder.

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

Das wars für die Bestätigung. Die Formularmodelllogik wirkt jedoch unübersichtlich und wiederholt. Lassen Sie uns das zuerst aufräumen. 

Umgestaltung des Codes mithilfe von FormBuilder

Angular bietet Ihnen einen Syntaxzucker zum Erstellen neuer Instanzen von FormGroup und FormControl mit dem Namen FormBuilder. Die FormBuilder-API macht nichts anderes als das, was wir hier behandelt haben.

Es vereinfacht unseren Code und macht das Erstellen einer Form für die Augen einfach. Um einen FormBuilder zu erstellen, müssen Sie ihn in importieren Anmeldeformular.component.ts und injizieren Sie den FormBuilder in den Konstruktor.

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

import FormBuilder, FormGroup, Validators aus '@ angle / forms';… export-Klasse SignupFormComponent implementiert OnInit signupForm: FormGroup; // Deklarieren Sie die signupForm // Injizieren Sie den Formbuilder in den Konstruktor-Konstruktor (private fb: FormBuilder)  ngOnInit () …

Anstatt ein neues zu schaffen Gruppe formen(), wir benutzen diese fb.gruppe ein Formular erstellen Bis auf die Syntax bleibt alles andere gleich.

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

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

Validierungsfehler anzeigen 

Für die Anzeige der Fehler verwende ich die bedingte Direktive ngIf auf einem div-Element. Beginnen wir mit dem Eingabefeld für die E-Mail:

 

Hier gibt es einige Probleme. 

  1. Wo hat ungültig und makellos komme aus? 
  2. signupForm.controls.email.invalid ist zu lang und tief.
  3. Der Fehler sagt nicht ausdrücklich aus, warum er ungültig ist.

Um die erste Frage zu beantworten, verfügt jedes FormControl über bestimmte Eigenschaften wie ungültig, gültig, makellos, schmutzig, berührt, und unberührt. Damit können wir feststellen, ob eine Fehlermeldung oder eine Warnung angezeigt werden soll oder nicht. Die Abbildung unten beschreibt jede dieser Eigenschaften im Detail.

Also das div Element mit dem * ngIf wird nur ausgegeben, wenn die E-Mail ungültig ist. Der Benutzer wird jedoch mit Fehlern in Bezug auf die leeren Eingabefelder begrüßt, bevor er das Formular bearbeiten kann. 

Um dieses Szenario zu vermeiden, haben wir die zweite Bedingung hinzugefügt. Der Fehler wird erst nach angezeigt Die Kontrolle wurde besucht.

Um die lange Kette von Methodennamen loszuwerden (signupForm.controls.email.invalid), Werde ich ein paar Abkürzungsmethoden hinzufügen. Dadurch bleiben sie zugänglich und kurz. 

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

export-Klasse SignupFormComponent implementiert OnInit … get email () return this.signupForm.get ('email');  get password () return this.signupForm.get ('password');  get gender () return this.signupForm.get ('gender');  get terms () return this.signupForm.get ('terms'); 

Um den Fehler deutlicher zu machen, habe ich folgende verschachtelte ngIf-Bedingungen hinzugefügt:

app / signup-form / signup-form.component.html

 
E-Mail-Feld darf nicht leer sein
Die E-Mail-ID scheint nicht richtig zu sein

Wir gebrauchen email.errors um alle möglichen Validierungsfehler zu überprüfen und sie dem Benutzer in Form von benutzerdefinierten Meldungen anzuzeigen. Führen Sie nun die gleichen Schritte für die anderen Formularelemente aus. So habe ich die Gültigkeitsprüfung für die Passwörter und die Eingabeeingabekontrolle programmiert.

app / signup-form / signup-form.component.html

  
Das Passwort muss aus mehr als 8 Zeichen bestehen
Bitte akzeptieren Sie zuerst die Allgemeinen Geschäftsbedingungen.

Senden Sie das Formular mit ngSubmit

Wir sind fast fertig mit der Form. Es fehlt die Submit-Funktionalität, die wir jetzt implementieren werden.

Beim Senden des Formulars sollten die Werte des Formularmodells in die Benutzereigenschaft der Komponente einfließen.

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

public onFormSubmit () if (this.signupForm.valid) this.user = this.signupForm.value; console.log (this.user); / * Jede API-Aufruflogik über Services geht hier * /

Verpacken

Wenn Sie diese Tutorialserie von Anfang an verfolgt haben, haben wir in Angular praktische Erfahrungen mit zwei populären Formbuilding-Technologien gesammelt. Die vorlagen- und modellgetriebenen Techniken sind zwei Möglichkeiten, um dasselbe zu erreichen. Ich persönlich bevorzuge die reaktiven Formen aus folgenden Gründen:

  • Die gesamte Formularvalidierungslogik befindet sich an einer Stelle innerhalb Ihrer Komponentenklasse. Dies ist wesentlich produktiver als der Vorlagenansatz, bei dem die ngModel-Direktiven über die Vorlage verteilt sind.
  • Im Gegensatz zu vorlagengesteuerten Formularen sind modellgesteuerte Formulare einfacher zu testen. Sie müssen nicht auf End-to-End-Testbibliotheken zurückgreifen, um Ihr Formular zu testen.
  • Die Validierungslogik geht in die Komponentenklasse und nicht in die Vorlage.
  • Für ein Formular mit einer großen Anzahl von Formularelementen verfügt dieser Ansatz über den Namen FormBuilder, um die Erstellung von FormControl-Objekten zu vereinfachen.

Wir haben eines übersehen, und das ist das Schreiben eines Prüfers für die Passwortabweichung. Im letzten Teil der Serie behandeln wir alles, was Sie zum Erstellen benutzerdefinierter Prüferfunktionen in Angular wissen müssen. Bleib dran bis dahin.

In der Zwischenzeit gibt es eine Vielzahl von Frameworks und Bibliotheken, mit denen Sie sich auf dem Laufenden halten können. Auf Envato Market finden Sie zahlreiche Artikel, die Sie lesen, studieren und verwenden können.