Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Angular password and confirm password validation

Suggested Videos
Part 24 - Angular value vs ngValue | Text | Slides
Part 25 - Angular custom validator example template driven forms | Text | Slides
Part 26 - Angular select list required custom validator | Text | Slides

In this video we will discuss how to compare password and confirm password fields and validate if they are equal. If they are not equal we want to display "Password and Confirm Password does not match" validation error.

angular confirm password validation


This is also commonly called as cross field validation in angular. We cannot use any of the buil-in angular validators to perform cross-field validation. So let's create a custom validator. To use a custom validator in template driven forms, we create the validator as a directive. We discussed creating custom validators and directives in Parts 25 and 26 of Angular CRUD tutorial. If you are new to these concepts, please check out those videos.

We will make this custom validator a reusable validator, so we could use it to compare any 2 input fields for equality. For example, we can use this same custom validator to compare if EMAIL and CONFIRM EMAIL fields are equal.


Create a custom Directive 
Add a new TypeScript file to the "shared" folder. Name it confirm-equal-validator.directive.ts.Copy and paste the folllowing code. 

import { Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
import { Directive, Input } from '@angular/core';

@Directive({
    selector: '[appConfirmEqualValidator]',
    providers: [{
        provide: NG_VALIDATORS,
        useExisting: ConfirmEqualValidatorDirective,
        multi: true
    }]
})
export class ConfirmEqualValidatorDirective implements Validator {
    @Input() appConfirmEqualValidator: string;
    validate(control: AbstractControl): { [key: string]: any } | null {
        const controlToCompare = control.parent.get(this.appConfirmEqualValidator);
        if (controlToCompare && controlToCompare.value !== control.value) {
            return { 'notEqual': true };
        }

        return null;
    }
}

Code Exaplanation : 
Since we are creating a directive, we decorate the class with @Directive decorator

This selector will be used as a directive on one of the 2 input fields that we want to compare for equality. In our case we will use it on the Confirm Password field.
selector: '[appConfirmEqualValidator]',

NG_VALIDATORS is a collection of validators. It contains all the built-in validators like required, pattern etc. Before we can use our custom validator we have to add it to the list of validators by adding it to NG_VALIDATORS token. To specify that we want to add our validator to the list of validators, we set multi property to true

providers: [{
    provide: NG_VALIDATORS,
    useExisting: ConfirmEqualValidatorDirective,
    multi: true
}]

Implement Validator interface as we are creating a custom validator
export class ConfirmEqualValidatorDirective implements Validator

Since our custom validator class is implementing validator interface, we have to provide implementation for the interface validate() method. This method has one input parameter and it's type is AbstractControl. AbstractControl extends both FormControl and FormGroup. In some case you may want to validate a Formgroup instead of a single FormControl. So to cater for both scenarios, the parent type - AbstractControl is specified. This function returns an object if the validation fails or null if the validation succeeds. The object that is returned when the validation fails contains a key/value pair. The key is a string and the value can be anything.
validate(control: AbstractControl): { [key: string]: any } | null

The following line creates an input property. Since this is a directive input property, the input property name and the selector name must match. 
@Input() appConfirmEqualValidator: string;

We will use this custom directive (appConfirmEqualValidator), as an attribute either on the PASSWORD field or CONFIRM PASSWORD FIELD. If we use this on the  CONFIRM PASSWORD field, we will also pass the field that we want to compare with. In this case the PASSWORD field.

So the input property that we have created above receives the control that we want to compare CONFIRM PASSWORD field with. This input property prevents the need to hard code the name of the control that we want to compare with. Hence it makes our custom validator reusable. We can use it to compare any 2 input fields for equality.

<input name="confirmPassword" appConfirmEqualValidator="password"
  • In the validate() method implementation, we first retrieve the control that we want to compare CONFIRM PASSWORD field with. That field in our case is the PASSWORD field.
  • Both PASSWORD and CONFIRM PASSWORD fields are siblings. So to get the PASSWORD field, we go one level up from the CONFIRM PASSWORD field using the parent property. The parent property returns the root FormGroup. 
  • On the root FormGroup we call the get() method passing it, the input property. The input property receives the name of the PASSWORD field
  • Finally we check if the PASSWORD and CONFIRM PASSWORD filed values are equal. If they are equal, we return NULL indication validation succeeded otherwise we return an object with key=notEqual and value=true.
  • In the HTML we can use this key (notEqual) to display the relevant validation error message
validate(control: AbstractControl): { [key: string]: any } | null {
    const controlToCompare = control.parent.get(this.appConfirmEqualValidator);
    if (controlToCompare && controlToCompare.value !== control.value) {
        return { 'notEqual': true };
    }

    return null;
}

Import the custom directive in a module where you want to use it. 

At the moment we only have one module - Root module. So in app.module.ts file include the following import statement
import { ConfirmEqualValidatorDirective } from './shared/confirm-equal-validator.directive';

Also include ConfirmEqualValidatorDirective in the declarations array of the NgModule() decorator

Using the custom validator

Include the following HTML for Password and Confirm Password fields in create-employee.component.html file as shown below.

<div class="form-group"
     [class.has-error]="password.touched && password.invalid">
  <label for="password" class="control-label">Password</label>
  <input id="password" required type="text" class="form-control"
         name="password" [(ngModel)]="employee.password"
         #password="ngModel">
  <span class="help-block"
        *ngIf="password.touched && password.errors?.required">
    Password is required
  </span>
</div>

<div class="form-group"
     [class.has-error]="confirmPassword.touched && confirmPassword.invalid">
  <label for="confirmPassword" class="control-label">Confirm Password</label>
  <input name="confirmPassword" appConfirmEqualValidator="password" required
         id="confirmPassword" type="text" class="form-control"
         [(ngModel)]="employee.confirmPassword" #confirmPassword="ngModel">
  <span class="help-block"
        *ngIf="confirmPassword.touched && confirmPassword.errors?.required">
    Confirm Password is required
  </span>
  <span class="help-block"
        *ngIf="confirmPassword.touched && confirmPassword.errors?.notEqual &&
          !confirmPassword.errors?.required">
    Password and Confirm Password does not match
  </span>
</div>

Notice on the CONFIRM PASSWORD field we are using our custom directive and passing it the name of the control (PASSWORD) that we want to compare with.
<input name="confirmPassword" appConfirmEqualValidator="password"

Notice the expression of *ngIf structural directive. We are using the key (notEqual) set by our custom validator to display the validation error message.
<span class="help-block"
      *ngIf="confirmPassword.touched && confirmPassword.errors?.notEqual &&
        !confirmPassword.errors?.required">
  Password and Confirm Password does not match
</span>

At the moment there are 2 problems with our custom validator
When the validation fails only the CONFIRM PASSWORD field is styled with red border and not the PASSWORD field. We want the password field also to have the red border.

Angular password and confirm password validation

If you first change PASSWORD field and then the CONFIRM PASSWORD field, the validation works as expected. Now if you go back and change the PASSWORD field, the validation will not be triggered and you will not see the validation error even if the passwords do not match. 

We will discuss why this is happening and how to fix it in our next video.

angular crud tutorial

2 comments:

  1. what is difference you use required and required id between password and confirmatpassword here?

    ReplyDelete
  2. Can`t we directly pass the employee.password property also in appConfirmEqualValidator directive

    [appConfirmEqualValidator]="employee.password"
    and then just check the control value to property value in directive
    return control.value !== this.appConfirmEqualValidator ? { 'notEqual': true } : null;

    i tried this and it worked for me.

    ReplyDelete

It would be great if you can help share these free resources