Angular Template Driven Form Passwords Validation
Introduction to Template-driven forms Validation with Smart UI
This page shows you how to use Custom Validators in Angular Template Driven Form to validate passwords. We use the ngModelGroup to a group of form controls. The ngModelGroup directive is used to validate a sub-group of our form separately from the rest of the form. That sub-group contains two password fields for entering and confirming the password.- Build an Angular form with a component and template.
- Use ngModel to create two-way data bindings for reading and writing input-control values.
- Validate user input and show validation errors to users and enable/disable form controls.
- Use template refeence variables.
Let's start
- ng new my-project
- cd my-project.
- ng add smart-webcomponents-angular.
- ng-serve and in your browser enter localhost:4200.
- Navigate to the \src\app folder.
- Create a new password.directive.ts
- Open app.module.ts and put the following content:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { DropDownListModule } from 'smart-webcomponents-angular/dropdownlist'; import { ButtonModule } from 'smart-webcomponents-angular/button'; import { InputModule } from 'smart-webcomponents-angular/input'; import { CheckPasswordDirective } from './password.directive'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent, CheckPasswordDirective], imports: [BrowserModule, FormsModule, DropDownListModule, ButtonModule, InputModule], bootstrap: [AppComponent] }) export class AppModule { }
- Open app.component.ts and put the following content:
import { Component, ViewChild, OnInit, AfterViewInit, OnChanges } from '@angular/core'; import { Directive, Input } from "@angular/core"; import { Validator, ValidationErrors, FormGroup, NG_VALIDATORS, NgForm, Validators } from "@angular/forms"; import { DropDownListComponent } from 'smart-webcomponents-angular/dropdownlist'; export class User { constructor( public id: number, public name: string, public gender: string, public phone: string, public password: string, public address: string ) { } } @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements AfterViewInit, OnInit { genderType = ['Female', 'Male']; password1; password2; model = new User(18, '', this.genderType[0], '', '', '123 Fake Street. Seattle, WA 98112'); submitted = false; onSubmit() { this.submitted = true; } ngOnInit(): void { // onInit code. } ngAfterViewInit(): void { // afterViewInit code. this.init(); } init(): void { // init code. } newUser() { this.model = new User(42, '', '', '', '', ''); } get diagnostic() { return JSON.stringify(this.model); } }
-
Open app.component.html and put the following content:
<div [hidden]="submitted" class="container"> <h1>User Registration</h1> <form (ngSubmit)="onSubmit()" #userForm="ngForm"> <div class="form-row"> <label for="name">Name</label> <smart-input required class="form-component" [(ngModel)]="model.name" name="name" id="name" #name="ngModel"> </smart-input> <div [hidden]="name.valid || name.pristine" class="alert alert-danger"> Name is required </div> </div> <div class="form-row"> <label for="address">Address</label> <smart-input class="form-component" [(ngModel)]="model.address" name="address" id="address"> </smart-input> </div> <div class="form-row"> <label for="phone">Phone</label> <smart-masked-text-box class="form-component" mask="+1 (###) ### - ####" [(ngModel)]="model.phone" name="phone" id="phone"> </smart-masked-text-box> </div> <div class="form-row" ngModelGroup="passwords" #passwords="ngModelGroup" appCheckPassword> <label for="passwordInput1">Password</label> <smart-password-text-box type="password" class="form-component" id="passwordInput1" placeholder="password" [(ngModel)]="password1" (change)="model.password=passwordA.value" #passwordA="ngModel" name="passwordA" required id="passwordA"> </smart-password-text-box> <label for="passwordInput2">Confirm Password</label> <smart-password-text-box type="password" class="form-component" id="passwordInput2" placeholder="confirm password" [(ngModel)]="password2" #passwordB="ngModel" name="passwordB" required id="passwordB"> </smart-password-text-box> </div> <div *ngIf="passwordB.invalid && (passwordA.dirty || passwordA.touched)" class="alert alert-danger"> Please confirm your password. </div> <div *ngIf="passwords.errors?.passwordCheck && (passwordB.dirty || passwordB.touched)" class="alert alert-danger"> Passwords do not match. </div> <div class="form-row"> <label for="gender">Gender</label> <smart-drop-down-list required #gender="ngModel" class="form-component" [(ngModel)]="model.gender" name="gender" id="gender" required> <smart-list-item *ngFor="let gender of genderType" [value]="gender">{{gender}}</smart-list-item> </smart-drop-down-list> <div [hidden]="gender.valid || gender.pristine" class="alert alert-danger"> Gender is required </div> </div> <div class="smart-stack-layout"> <smart-button [disabled]="!userForm.form.valid" type="submit" class="success item">Submit</smart-button> <smart-button type="submit" (click)="newUser(); userForm.reset()" class="primary item">New User </smart-button> </div> </form> </div> <div [hidden]="!submitted"> <h2>You submitted the following:</h2> <div class="row"> <div class="col-xs-3">Name</div> <div class="col-xs-9">{{ model.name }}</div> </div> <div class="row"> <div class="col-xs-3">Address</div> <div class="col-xs-9">{{ model.address }}</div> </div> <div class="row"> <div class="col-xs-3">Phone</div> <div class="col-xs-9">{{ model.phone }}</div> </div> <div class="row"> <div class="col-xs-3">Password</div> <div class="col-xs-9">{{ model.password }}</div> </div> <div class="row"> <div class="col-xs-3">Gender</div> <div class="col-xs-9">{{ model.gender }}</div> </div> <br> <smart-button class="primary" (click)="submitted=false">Edit</smart-button> </div> <br /><br /> <h2>Changes Log:</h2> {{diagnostic}}
In the code above, you can see that the validator is associated with the group passwords and not passwordA or passwordB, the validation error is associated with the group and not the controls. By checking for errors in the group passwords, we alert the user, whether the passwords match or not.
import { Directive } from '@angular/core'; import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from "@angular/forms"; function validatePassword(): ValidatorFn { return (control: AbstractControl) => { let isValid = false; if (control && control instanceof FormGroup) { let group = control as FormGroup; if (group.controls['passwordA'] && group.controls['passwordB']) { isValid = group.controls['passwordA'].value == group.controls['passwordB'].value; } } if (isValid) { return null; } else { return { 'passwordCheck': 'failed' } } } } @Directive({ selector: '[appCheckPassword]', providers: [{ provide: NG_VALIDATORS, useExisting: CheckPasswordDirective, multi: true }] }) export class CheckPasswordDirective implements Validator { private valFn; constructor() { this.valFn = validatePassword(); } validate(c: AbstractControl): ValidationErrors | null { return this.valFn(c); } }
The validator searches the group for a control named passwordA and a control named passwordB and checks that their values are the same. If they are not the same, it returns a custom validation error named “passwordCheck”.
ngModel
The Two-way data binding is achieved with ngModel. Note the [(ngModel)]="model.name", [(ngModel)]="model.alterEgo", [(ngModel)]="model.power" in the template above.You also need a template reference variable to access the input box's Angular control from within the template.
<smart-input required class="form-component" [(ngModel)]="model.name" name="name" id="name" #name="ngModel"></smart-input>Here you created a variable called name and gave it the value "ngModel".