Beim Studium von Angular FormBuilder ist mir aufgefallen, dass man Gruppen verschachteln kann. Auf Anhieb war jedoch nicht klar, warum das gut sein soll.
Der Trick ist, dass man einzelne Gruppen validieren kann.
Ein typisches Beispiel ist eine Benutzer-Registrierung, welche das Passwort und die Passwort-Bestätigung benötigt. Die Passwort-Bestätigung muss natürlich identisch zum Passwort sein.
FormBuilder und andere benötigte Components importieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators, AbstractControl, FormControl } @Component({ selector: 'page-register', templateUrl: 'register.html' }) export class RegisterPage { registerForm: FormGroup; username: AbstractControl; email: AbstractControl; passwordGroup: FormGroup; password: AbstractControl; confirmPassword: AbstractControl; |
Danach die Gruppe für die abhängigen Felder definieren. Hier die beiden Passwort-Felder. Am Schluss als letzten Parameter den „Validator“ anhängen.
1 2 3 4 |
this.passwordGroup = this.formBuilder.group({ 'password': ['', [Validators.required, Validators.minLength(6)]], 'confirmPassword': ['', Validators.required] }, {validator: this.validatePassword}); |
Dann die Gruppe an die erste Gruppe hängen.
1 2 3 4 5 |
this.registerForm = this.formBuilder.group({ 'username': ['', Validators.required], 'email': ['',[Validators.required, EmailValidator.valid], this.validateUniqueEmail.bind(this)], 'passwords': this.passwordGroup }); |
Der erste Vorteil ist nun, dass bei der Validierung nur die Felder der Gruppe übergeben werden.
1 2 3 4 5 6 7 8 9 |
validatePassword(group: FormGroup) { if (group.controls['password'].value != group.controls['confirmPassword'].value) { return {passwordMismatch: true} } else { return null; } } |
Ist das alles?
Natürlich nicht. Denn die Validierung hätte mann auch auch machen können, wenn die ganze Formgruppe übergeben worden wäre. Schön ist nun, dass im UI auch Fehler auf Gruppen-Ebene angezeigt werden können.
1 2 3 4 5 |
<ion-item [class.error]="!confirmPassword.valid && confirmPassword.touched"> <ion-label floating>Confirm Password</ion-label> <ion-input type="password" name="confirmPassword" [formControl]="confirmPassword" spellcheck="false" autocapitalize="off"></ion-input> </ion-item> <div *ngIf="!passwordGroup.valid && password.valid && confirmPassword.touched" class="error-box">Passwords do not match</div><br> |
Und natürlich ist auch die ganze Formgruppe, nur valid, wenn alle Untergruppen valid sind.
1 2 |
<button ion-button block type="submit" [disabled]="!registerForm.valid">Register</button> </form> |
Man beachte, dass die Gruppe als Directive angegeben wird
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<form [formGroup]="registerForm" (ngSubmit)="submit(registerForm.value)"> <ion-item [class.error]="!username.valid && username.touched"> <ion-label floating>Your Name</ion-label> <ion-input type="text" name="username" [formControl]="username" spellcheck="false"></ion-input> </ion-item> <div *ngIf="username.hasError('required') && username.touched" class="error-box">Name is required</div> <ion-item [class.error]="!email.valid && !email.pending"> <ion-label floating>Email<span *ngIf="email.pending"> (checking...)</span></ion-label> <ion-input type="email" name="email" [formControl]="email" spellcheck="false" autocapitalize="off"></ion-input> </ion-item> <div *ngIf="email.hasError('required') && email.touched" class="error-box">Email is required</div> <div *ngIf="email.hasError('validateEmail') && email.touched" class="error-box">Invalid Email address</div> <div *ngIf="email.hasError('emailAddressAlreadyRegistered') && !email.pending" class="error-box">Email not allowed.</div> <div [formGroup]="passwordGroup"> <ion-item [class.error]="!password.valid && password.touched"> <ion-label floating>Password</ion-label> <ion-input type="password" name="password" [formControl]="password" spellcheck="false" autocapitalize="off"></ion-input> </ion-item> <div *ngIf="password.hasError('required') && password.touched" class="error-box">Password is required</div> <div *ngIf="password.hasError('minlength') && password.touched" class="error-box">Password minimum Length is 6</div> <ion-item [class.error]="!confirmPassword.valid && confirmPassword.touched"> <ion-label floating>Confirm Password</ion-label> <ion-input type="password" name="confirmPassword" [formControl]="confirmPassword" spellcheck="false" autocapitalize="off"></ion-input> </ion-item> <div *ngIf="!passwordGroup.valid && password.valid && confirmPassword.touched" class="error-box">Passwords do not match</div> </div> <br><br> <button ion-button block type="submit" [disabled]="!registerForm.valid">Register</button> </form> |