Confirm password validation in Angular 6

AngularAngular MaterialAngular Forms

Angular Problem Overview


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.

Angular Solutions


Solution 1 - Angular

This question could be solved with a combination of these two answers: https://stackoverflow.com/a/43493648/6294072 and https://stackoverflow.com/a/47670892/6294072

So first of all, you would need a custom validator for checking the passwords, that could look like this:

checkPasswords: ValidatorFn = (group: AbstractControl):  ValidationErrors | null => { 
  let pass = group.get('password').value;
  let confirmPass = group.get('confirmPassword').value
  return pass === confirmPass ? null : { notSame: true }
}

and you would create a formgroup for your fields, instead of just two form controls, then mark that custom validator for your form group:

this.myForm = this.fb.group({
  password: ['', [Validators.required]],
  confirmPassword: ['']
}, { validators: this.checkPasswords })

and then as mentioned in other answer, the mat-error only shows if a FormControl is invalid, so you need an error state matcher:

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

    return invalidCtrl || invalidParent;
  }
}

in the above you can tweak when to show error message. I would only show message when the password field is touched. Also I would like above, remove the required validator from the confirmPassword field, since the form is not valid anyway if passwords do not match.

Then in component, create a new ErrorStateMatcher:

matcher = new MyErrorStateMatcher();

Finally, the template would look like this:

<form [formGroup]="myForm">
  <mat-form-field>
	<input matInput placeholder="New password" formControlName="password" required>
	<mat-error *ngIf="myForm.hasError('required', 'password')">
	  Please enter your new password
	</mat-error>
  </mat-form-field>

  <mat-form-field>
	<input matInput placeholder="Confirm password" formControlName="confirmPassword" [errorStateMatcher]="matcher">
  	<mat-error *ngIf="myForm.hasError('notSame')">
	  Passwords do not match
	</mat-error>  
  </mat-form-field>
</form>

Here's a demo for you with the above code: StackBlitz

Solution 2 - Angular

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>

Solution 3 - Angular

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.

Solution 4 - Angular

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" (change)="onPasswordChange()"/>

// 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>

Solution 5 - Angular

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!

Solution 6 - Angular

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);

Solution 7 - Angular

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

Solution 8 - Angular

*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>

Solution 9 - Angular

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');
  }

Solution 10 - Angular

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.

Solution 11 - Angular

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>

Solution 12 - Angular

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**
}

Solution 13 - Angular

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;
      }
    }

  

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShankarView Question on Stackoverflow
Solution 1 - AngularAT82View Answer on Stackoverflow
Solution 2 - AngularAlok MaliView Answer on Stackoverflow
Solution 3 - AngularZahemaView Answer on Stackoverflow
Solution 4 - AngularWasiFView Answer on Stackoverflow
Solution 5 - AngularCelso SoaresView Answer on Stackoverflow
Solution 6 - AngularHenryView Answer on Stackoverflow
Solution 7 - AngularNiclauselView Answer on Stackoverflow
Solution 8 - AngulargoldenbearkinView Answer on Stackoverflow
Solution 9 - AngularThe Gilbert Arenas DaggerView Answer on Stackoverflow
Solution 10 - AngularDavid PrietoView Answer on Stackoverflow
Solution 11 - AngularAjay GuptaView Answer on Stackoverflow
Solution 12 - AngularMukul KashyapView Answer on Stackoverflow
Solution 13 - AngularSushilView Answer on Stackoverflow