[angular] Confirm password validation in Angular 6

I want to perform password and confirm password validations using material components only,and an error message below the confirm password field if confirm password field doesn't match And if it is empty.Tried many resources unable to achieve.

Tried this video too.

This is the material component i am looking for

enter image description here

HTML

     <mat-form-field >
        <input matInput  placeholder="New password" [type]="hide ? 'password' 
          : 'text'" [formControl]="passFormControl" required>
        <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 
          'visibility_off'}}</mat-icon>
        <mat-error *ngIf="passFormControl.hasError('required')">
            Please enter your newpassword
         </mat-error>
      </mat-form-field>

      <mat-form-field >
         <input matInput  placeholder="Confirm password" [type]="hide ? 
              'password' : 'text'" [formControl]="confirmFormControl" 
                    required>
         <mat-icon matSuffix (click)="hide = !hide">{{hide ? 'visibility' : 
                'visibility_off'}}</mat-icon>
         <mat-error *ngIf="confirmFormControl.hasError('required')">
          Confirm your password
          </mat-error>
      </mat-form-field>

TS

     import {Component, OnInit } from '@angular/core';
     import {FormControl, FormGroupDirective, NgForm, Validators} from 
             '@angular/forms';
     import {ErrorStateMatcher} from '@angular/material/core';

     @Component({
            selector: 'asd-set-pass',
            templateUrl: './set-pass.component.html',
             styleUrls: ['./set-pass.component.css']
         })

       passFormControl = new FormControl('', [
            Validators.required,
        ]);
        confirmFormControl = new FormControl('', [
            Validators.required,
            ]);

             hide =true;

       }

It's validating the following conditions fine 1)If password and confirm password fields are empty its showing error text.

I want to compare to fields in (.ts) file like how its validating for empty field, and an error to come if confirm password field is empty.

This question is related to angular angular-material angular-forms

The answer is


You can use this way to fulfill this requirement. I use the below method to validate the Password and Confirm Password.

To use this method you have to import FormGroup from @angular/forms library.

import { FormBuilder, Validators, FormGroup } from '@angular/forms';

FormBuilder Group:

this.myForm= this.formBuilder.group({
  password    : ['', Validators.compose([Validators.required])],
  confirmPassword    : ['',  Validators.compose([Validators.required])],
},
{validator: this.checkPassword('password', 'confirmPassword') }
);

Method to Validate two fields:

 checkPassword(controlName: string, matchingControlName: string) {
    return (formGroup: FormGroup) => {
        const control = formGroup.controls[controlName];
        const matchingControl = formGroup.controls[matchingControlName];
        if (matchingControl.errors && !matchingControl.errors.mustMatch) {
            // return if another validator has already found an error on the matchingControl
            return;
        }
        // set error on matchingControl if validation fails
        if (control.value !== matchingControl.value) {
            matchingControl.setErrors({ mustMatch: true });
            this.isPasswordSame = (matchingControl.status == 'VALID') ? true : false;
        } else {
            matchingControl.setErrors(null);
            this.isPasswordSame = (matchingControl.status == 'VALID') ? true : false;
        }
    }
  }

HTML: Here I am use personalized isPasswordSame variable you can use the inbuilt hasError or any other.

<form [formGroup]="myForm">
  <ion-item>
    <ion-label position="floating">Password</ion-label>
    <ion-input required type="text" formControlName="password" placeholder="Enter Password"></ion-input>
  </ion-item>
  <ion-label *ngIf="myForm.controls.password.valid">
      <p class="error">Please enter password!!</p>
  </ion-label>
  <ion-item>
    <ion-label position="floating">Confirm Password</ion-label>
    <ion-input required type="text" formControlName="confirmPassword" placeholder="Enter Confirm Password"></ion-input>
  </ion-item>
  <ion-label *ngIf="isPasswordSame">
      <p class="error">Password and Confrim Password must be same!!</p>
  </ion-label>
</form>

Single method for Reactive Forms

TYPESCRIPT

// All is this method
onPasswordChange() {
  if (this.confirm_password.value == this.password.value) {
    this.confirm_password.setErrors(null);
  } else {
    this.confirm_password.setErrors({ mismatch: true });
  }
}

// getting the form control elements
get password(): AbstractControl {
  return this.form.controls['password'];
}

get confirm_password(): AbstractControl {
  return this.form.controls['confirm_password'];
}

HTML

// PASSWORD FIELD
<input type="password" formControlName="password" />

// CONFIRM PASSWORD FIELD
<input type="password" formControlName="confirm_password" (change)="onPasswordChange()" />

// SHOW ERROR IF MISMATCH
<span *ngIf="confirm_password.hasError('mismatch')">Password do not match.</span>

It's not necessary to use nested form groups and a custom ErrorStateMatcher for confirm password validation. These steps were added to facilitate coordination between the password fields, but you can do that without all the overhead.

Here is an example:

this.registrationForm = this.fb.group({
  username: ['', Validators.required],
  email: ['', [Validators.required, Validators.email]],
  password1: ['', [Validators.required, (control) => this.validatePasswords(control, 'password1') ] ],
  password2: ['', [Validators.required, (control) => this.validatePasswords(control, 'password2') ] ]
});

Note that we are passing additional context to the validatePasswords method (whether the source is password1 or password2).

  validatePasswords(control: AbstractControl, name: string) {
    if (this.registrationForm === undefined || this.password1.value === '' || this.password2.value === '') {
      return null;
    } else if (this.password1.value === this.password2.value) {
      if (name === 'password1' && this.password2.hasError('passwordMismatch')) {
        this.password1.setErrors(null);
        this.password2.updateValueAndValidity();
      } else if (name === 'password2' && this.password1.hasError('passwordMismatch')) {
        this.password2.setErrors(null);
        this.password1.updateValueAndValidity();
      }
      return null;
    } else {
      return {'passwordMismatch': { value: 'The provided passwords do not match'}};
    }  

Note here that when the passwords match, we coordinate with the other password field to have its validation updated. This will clear any stale password mismatch errors.

And for completeness sake, here are the getters that define this.password1 and this.password2.

  get password1(): AbstractControl {
    return this.registrationForm.get('password1');
  }

  get password2(): AbstractControl {
    return this.registrationForm.get('password2');
  }

I found a bug in AJT_82's answer. Since I do not have enough reputation to comment under AJT_82's answer, I have to post the bug and solution in this answer.

Here is the bug:

enter image description here

Solution: In the following code:

export class MyErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    const invalidCtrl = !!(control && control.invalid && control.parent.dirty);
    const invalidParent = !!(control && control.parent && control.parent.invalid && control.parent.dirty);

    return (invalidCtrl || invalidParent);
  }
}

Change control.parent.invalid to control.parent.hasError('notSame') will solve this problem.

After the small changes, the problem solved.

enter image description here

Edit: To validate the Confirm Password field only after the user starts typing you can return this instead

return ((invalidCtrl || invalidParent) && control.valid);

My answer is very simple>i have created password and confirm password validation using template driiven in angular 6

My html file

<div class="form-group">
  <label class="label-sm">Confirm Password</label>
  <input class="form-control" placeholder="Enter Password" type="password" #confirm_password="ngModel" [(ngModel)]="userModel.confirm_password" name="confirm_password" required (keyup)="checkPassword($event)" />
  <div *ngIf="confirm_password.errors && (confirm_password.dirty||confirm_password.touched||signup.submitted)">
  <div class="error" *ngIf="confirm_password.errors.required">Please confirm your password</div>
  </div>
  <div *ngIf="i" class='error'>Password does not match</div>
</div>

My typescript file

      public i: boolean;

      checkPassword(event) {
        const password = this.userModel.password;
        const confirm_new_password = event.target.value;

        if (password !== undefined) {
          if (confirm_new_password !== password) {
            this.i = true;
          } else {
            this.i = false;
          }
        }
      }

when clicking on submit button i check whether value of i is true or false

if true

if (this.i) {
      return false;
    }

else{
**form submitted code comes here**
}

The simplest way imo:

(It can also be used with emails for example)

public static matchValues(
    matchTo: string // name of the control to match to
  ): (AbstractControl) => ValidationErrors | null {
    return (control: AbstractControl): ValidationErrors | null => {
      return !!control.parent &&
        !!control.parent.value &&
        control.value === control.parent.controls[matchTo].value
        ? null
        : { isMatching: false };
    };
}

In your Component:

this.SignUpForm = this.formBuilder.group({

password: [undefined, [Validators.required]],
passwordConfirm: [undefined, 
        [
          Validators.required,
          matchValues('password'),
        ],
      ],
});

Follow up:

As others pointed out in the comments, if you fix the error by fixing the password field the error won't go away, Because the validation triggers on passwordConfirm input. This can be fixed by a number of ways. I think the best is:

this.SignUpForm .controls.password.valueChanges.subscribe(() => {
  this.SignUpForm .controls.confirmPassword.updateValueAndValidity();
});

On password change, revliadte confirmPassword.


I am using angular 6 and I have been searching on best way to match password and confirm password. This can also be used to match any two inputs in a form. I used Angular Directives. I have been wanting to use them

ng g d compare-validators --spec false and i will be added in your module. Below is the directive

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

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[compare]',
  providers: [{ provide: NG_VALIDATORS, useExisting: CompareValidatorDirective, multi: true}]
})
export class CompareValidatorDirective implements Validator {
  // tslint:disable-next-line:no-input-rename
  @Input('compare') controlNameToCompare;

  validate(c: AbstractControl): ValidationErrors | null {
    if (c.value.length < 6 || c.value === null) {
      return null;
    }
    const controlToCompare = c.root.get(this.controlNameToCompare);

    if (controlToCompare) {
      const subscription: Subscription = controlToCompare.valueChanges.subscribe(() => {
        c.updateValueAndValidity();
        subscription.unsubscribe();
      });
    }

    return controlToCompare && controlToCompare.value !== c.value ? {'compare': true } : null;
  }

}

Now in your component

<div class="col-md-6">
              <div class="form-group">
                <label class="bmd-label-floating">Password</label>
                <input type="password" class="form-control" formControlName="usrpass" [ngClass]="{ 'is-invalid': submitAttempt && f.usrpass.errors }">
                <div *ngIf="submitAttempt && signupForm.controls['usrpass'].errors" class="invalid-feedback">
                  <div *ngIf="signupForm.controls['usrpass'].errors.required">Your password is required</div>
                  <div *ngIf="signupForm.controls['usrpass'].errors.minlength">Password must be at least 6 characters</div>
                </div>
              </div>
            </div>
            <div class="col-md-6">
              <div class="form-group">
                <label class="bmd-label-floating">Confirm Password</label>
                <input type="password" class="form-control" formControlName="confirmpass" compare = "usrpass"
                [ngClass]="{ 'is-invalid': submitAttempt && f.confirmpass.errors }">
                <div *ngIf="submitAttempt && signupForm.controls['confirmpass'].errors" class="invalid-feedback">
                  <div *ngIf="signupForm.controls['confirmpass'].errors.required">Your confirm password is required</div>
                  <div *ngIf="signupForm.controls['confirmpass'].errors.minlength">Password must be at least 6 characters</div>
                  <div *ngIf="signupForm.controls['confirmpass'].errors['compare']">Confirm password and Password dont match</div>
                </div>
              </div>
            </div>

I hope this one helps


Just do a standard custom validator and verify first if the form itself is defined, otherwise it will throw an error that says the form is undefined, because at first it will try to run the validator before the form is constructed.

// form builder
private buildForm(): void {
    this.changePasswordForm = this.fb.group({
        currentPass: ['', Validators.required],
        newPass: ['', Validators.required],
        confirmPass: ['', [Validators.required, this.passwordMatcher.bind(this)]],
    });
}

// confirm new password validator
private passwordMatcher(control: FormControl): { [s: string]: boolean } {
    if (
        this.changePasswordForm &&
        (control.value !== this.changePasswordForm.controls.newPass.value)
    ) {
        return { passwordNotMatch: true };
    }
    return null;
}

It just checks that the new password field has the same value that the confirm password field. Is a validator specific for the confirm password field instead of the whole form.

You just have to verify that this.changePasswordForm is defined because otherwise it will throw an undefined error when the form is built.

It works just fine, without creating directives or error state matchers.


*This solution is for reactive-form

You may have heard the confirm password is known as cross-field validation. While the field level validator that we usually write can only be applied to a single field. For cross-filed validation, you probably have to write some parent level validator. For specifically the case of confirming password, I would rather do:

this.form.valueChanges.subscribe(field => {
  if (field.password !== field.confirm) {
    this.confirm.setErrors({ mismatch: true });
  } else {
    this.confirm.setErrors(null);
  }
});

And here is the template:

<mat-form-field>
      <input matInput type="password" placeholder="Password" formControlName="password">
      <mat-error *ngIf="password.hasError('required')">Required</mat-error>
</mat-form-field>
<mat-form-field>
    <input matInput type="password" placeholder="Confirm New Password" formControlName="confirm">`enter code here`
    <mat-error *ngIf="confirm.hasError('mismatch')">Password does not match the confirm password</mat-error>
</mat-form-field>

You can simply use password field value as a pattern for confirm password field. For Example :

<div class="form-group">
 <input type="password" [(ngModel)]="userdata.password" name="password" placeholder="Password" class="form-control" required #password="ngModel" pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}" />
 <div *ngIf="password.invalid && (myform.submitted || password.touched)" class="alert alert-danger">
   <div *ngIf="password.errors.required"> Password is required. </div>
   <div *ngIf="password.errors.pattern"> Must contain at least one number and one uppercase and lowercase letter, and at least 8 or more characters.</div>
 </div>
</div>

<div class="form-group">
 <input type="password" [(ngModel)]="userdata.confirmpassword" name="confirmpassword" placeholder="Confirm Password" class="form-control" required #confirmpassword="ngModel" pattern="{{ password.value }}" />
 <div *ngIf=" confirmpassword.invalid && (myform.submitted || confirmpassword.touched)" class="alert alert-danger">
   <div *ngIf="confirmpassword.errors.required"> Confirm password is required. </div>
   <div *ngIf="confirmpassword.errors.pattern"> Password & Confirm Password does not match.</div>
 </div>
</div>

I did it like this. hope this will help you.

HTML :

<form [formGroup]='addAdminForm'>   
          <div class="form-group row">
            <label class="col-sm-3 col-form-label">Password</label>
            <div class="col-sm-7">
              <input type="password" class="form-control" formControlName='password' (keyup)="checkPassSame()">

              <div *ngIf="addAdminForm.controls?.password?.invalid && addAdminForm.controls?.password.touched">
                <p *ngIf="addAdminForm.controls?.password?.errors.required" class="errorMsg">*This field is required.</p>
              </div>
            </div>
          </div>

          <div class="form-group row">
            <label class="col-sm-3 col-form-label">Confirm Password</label>
            <div class="col-sm-7">
              <input type="password" class="form-control" formControlName='confPass' (keyup)="checkPassSame()">

              <div *ngIf="addAdminForm.controls?.confPass?.invalid && addAdminForm.controls?.confPass.touched">
                <p *ngIf="addAdminForm.controls?.confPass?.errors.required" class="errorMsg">*This field is required.</p>
              </div>
              <div *ngIf="passmsg != '' && !addAdminForm.controls?.confPass?.errors?.required">
                <p class="errorMsg">*{{passmsg}}</p>
              </div>  
            </div>
          </div>
      </form>

TS File :

export class AddAdminAccountsComponent implements OnInit {

  addAdminForm: FormGroup;
  password: FormControl;
  confPass: FormControl;
  passmsg: string;


  constructor(
    private http: HttpClient,
    private router: Router,
  ) { 
  }

  ngOnInit() {    
    this.createFormGroup();
  }



    // |---------------------------------------------------------------------------------------
    // |------------------------ form initialization -------------------------
    // |---------------------------------------------------------------------------------------
    createFormGroup() {    
      this.addAdminForm = new FormGroup({
        password: new FormControl('', [Validators.required]),
        confPass: new FormControl('', [Validators.required]),
      })
    }



    // |---------------------------------------------------------------------------------------
    // |------------------------ Check method for password and conf password same or not -------------------------
    // |---------------------------------------------------------------------------------------

    checkPassSame() {
      let pass = this.addAdminForm.value.password;
      let passConf = this.addAdminForm.value.confPass;
      if(pass == passConf && this.addAdminForm.valid === true) {
        this.passmsg = "";
        return false;
      }else {
        this.passmsg = "Password did not match.";
        return true;
      }
    }



}

In case you have more than just Password and Verify Password fields. Like this the Confirm password field will only highlights error when user write something on this field:

validators.ts

import { FormGroup, FormControl, Validators, FormBuilder, FormGroupDirective, NgForm } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material/core';

export const EmailValidation = [Validators.required, Validators.email];
export const PasswordValidation = [
  Validators.required,
  Validators.minLength(6),
  Validators.maxLength(24),
];

export class RepeatPasswordEStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    return (control && control.parent.get('password').value !== control.parent.get('passwordAgain').value && control.dirty)
  }
}
export function RepeatPasswordValidator(group: FormGroup) {
  const password = group.controls.password.value;
  const passwordConfirmation = group.controls.passwordAgain.value;

  return password === passwordConfirmation ? null : { passwordsNotEqual: true }     
}

register.component.ts

import { FormGroup, FormControl, Validators, FormBuilder} from '@angular/forms';
import { EmailValidation, PasswordValidation, RepeatPasswordEStateMatcher, RepeatPasswordValidator } from 'validators';

...

form: any;
passwordsMatcher = new RepeatPasswordEStateMatcher;


constructor(private formBuilder: FormBuilder) {
    this.form = this.formBuilder.group ( {
      email: new FormControl('', EmailValidation),
      password: new FormControl('', PasswordValidation),
      passwordAgain: new FormControl(''),
      acceptTerms: new FormControl('', [Validators.requiredTrue])
    }, { validator: RepeatPasswordValidator });
  }

...

register.component.html

<form [formGroup]="form" (ngSubmit)="submitAccount(form)">
    <div class="form-content">
        <div class="form-field">
            <mat-form-field>
            <input matInput formControlName="email" placeholder="Email">
            <mat-error *ngIf="form.get('email').hasError('required')">
                E-mail is mandatory.
            </mat-error>
            <mat-error *ngIf="form.get('email').hasError('email')">
                Incorrect E-mail.
            </mat-error>
            </mat-form-field>
        </div>
        <div class="form-field">
            <mat-form-field>
            <input matInput formControlName="password" placeholder="Password" type="password">
            <mat-hint class="ac-form-field-description">Between 6 and 24 characters.</mat-hint>
            <mat-error *ngIf="form.get('password').hasError('required')">
                Password is mandatory.
            </mat-error>
            <mat-error *ngIf="form.get('password').hasError('minlength')">
                Password with less than 6 characters.
            </mat-error>
            <mat-error *ngIf="form.get('password').hasError('maxlength')">
                Password with more than 24 characters.
            </mat-error>
            </mat-form-field>
        </div>
        <div class="form-field">
            <mat-form-field>
            <input matInput formControlName="passwordAgain" placeholder="Confirm the password" type="password" [errorStateMatcher]="passwordsMatcher">
            <mat-error *ngIf="form.hasError('passwordsNotEqual')" >Passwords are different. They should be equal!</mat-error>
            </mat-form-field>
        </div>
        <div class="form-field">
            <mat-checkbox name="acceptTerms" formControlName="acceptTerms">I accept terms and conditions</mat-checkbox>
        </div>
    </div>
    <div class="form-bottom">
        <button mat-raised-button [disabled]="!form.valid">Create Account</button>
    </div>
</form>

i hope it helps!


Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7

Examples related to angular-material

error TS1086: An accessor cannot be declared in an ambient context in Angular 9 @angular/material/index.d.ts' is not a module How to set width of mat-table column in angular? Confirm password validation in Angular 6 Set default option in mat-select Can't bind to 'dataSource' since it isn't a known property of 'table' Checkbox angular material checked by default How to set the color of an icon in Angular Material? Disable click outside of angular material dialog area to close the dialog (With Angular Version 4.0+) Styling mat-select in Angular Material

Examples related to angular-forms

Confirm password validation in Angular 6 Set focus on <input> element How to use Angular4 to set focus by element id Cannot find control with name: formControlName in angular reactive form Angular error: "Can't bind to 'ngModel' since it isn't a known property of 'input'" Can't bind to 'formControl' since it isn't a known property of 'input' - Angular2 Material Autocomplete issue Angular2 If ngModel is used within a form tag, either the name attribute must be set or the form