Password match validation in reactive form in Angular

Viewed 10073

I have two inputs (newPassword and confirmPassword) which must be validated by matching them. I have resetPasswordFormGroup which is defined in reset-password.component.

When I use MustMatch custom validator in my formgroup I get this error. Actually, I'm confused about how to use the custom validator.

Argument of type '{ validator: (formGroup: FormGroup) => void; }' is not assignable to parameter of type 'ValidatorFn | ValidatorFn[] | AbstractControlOptions'. Object literal may only specify known properties, and 'validator' does not exist in type 'ValidatorFn | ValidatorFn[] | AbstractControlOptions'.ts(2345)

resetPasswordFormGroup = new FormGroup({
        currentPassword: new FormControl(null, Validators.required),
        newPassword: new FormControl(null, Validators.required),
        confirmNewPassword: new FormControl(null, Validators.required),
    });

    get currentPasswordFormControl() {
        return this.resetPasswordFormGroup.get('currentPassword');
    }

    get newPasswordFormControl() {
        return this.resetPasswordFormGroup.get('newPassword');
    }

    get confirmNewPasswordFormControl() {
        return this.resetPasswordFormGroup.get('confirmNewPassword');
    }
        <form [formGroup]='resetPasswordFormGroup' (ngSubmit)="onPasswordChange()">
            <mat-card-content>
                <mat-form-field class="center-text">
                    <input matInput placeholder="please enter your current password" formControlName="currentPassword" />
                    <mat-error *ngIf="currentPasswordFormControl.hasError('required')">
                        <strong>current password</strong> is required
                    </mat-error>
                </mat-form-field>
                <mat-form-field class="center-text">
                    <input matInput placeholder="please enter your new password" formControlName="newPassword" />
                    <mat-error *ngIf="newPasswordFormControl.hasError('required')">
                        <strong> new password </strong> is required  
                    </mat-error>
                </mat-form-field>
                <mat-form-field class="center-text">
                    <input matInput placeholder="please confirm your new password"
                        formControlName="confirmNewPassword" />
                    <mat-error *ngIf="confirmNewPasswordFormControl.hasError('required')">
                         <strong> confirm new password</strong> is required
                    </mat-error>
                </mat-form-field>
            </mat-card-content>

            <mat-card-actions>
                <button mat-raised-button color="primary">
                    <div *ngIf="isSubmitting" class="loading">
                        <mat-spinner [diameter]="26" color="accent"></mat-spinner>
                    </div>
                    <span *ngIf="!isSubmitting">
                        submit
                    </span>
                </button>
            </mat-card-actions>
        </form>

enter image description here

3 Answers

You kinda doing it wrongly

according to docs you have to do it like following:

const form = new FormGroup({
  password: new FormControl('', Validators.minLength(2)),
  passwordConfirm: new FormControl('', Validators.minLength(2)),
}, passwordMatchValidator);


function passwordMatchValidator(g: FormGroup) {
   return g.get('password').value === g.get('passwordConfirm').value
      ? null : {'mismatch': true};
}

where you pass the function by reference

or like:

const form = new FormGroup({
  password: new FormControl('')
  passwordConfirm: new FormControl('')
}, { validators: passwordMatchValidator, asyncValidators: otherValidator });

As well as, if you still want to do it with passing controls names, you might come up with the following

For match-password.validator.ts

import { FormGroup } from '@angular/forms';
    
export function ConfirmedValidator(controlName: string, matchingControlName: string){
    return (formGroup: FormGroup) => {
        const control = formGroup.controls[controlName];
        const matchingControl = formGroup.controls[matchingControlName];
        if (matchingControl.errors && !matchingControl.errors.confirmedValidator) {
            return;
        }
        if (control.value !== matchingControl.value) {
            matchingControl.setErrors({ confirmedValidator: true });
        } else {
            matchingControl.setErrors(null);
        }
    }
}

then you can import it and use it

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators} from '@angular/forms';
  
import { ConfirmedValidator } from './match-password.validator';
    
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  form: FormGroup = new FormGroup({});
  
  constructor(private fb: FormBuilder) {
  
    this.form = fb.group({
      password: ['', [Validators.required]],
      confirm_password: ['', [Validators.required]]
    }, { 
      validator: ConfirmedValidator('newPassword', 'confirmNewPassword')
    })
  }
    
  get f(){
    return this.form.controls;
  }
}

See Angular Validation Password and Confirm Password

That's not working for me, I tried multiple times but I couldn't figure out why it doesn't work. Finally, I did it in this way. Thank you, guys.

resetPasswordFormGroup: FormGroup;
matcher = new MyErrorStateMatcher();

    constructor(
        private router: Router,
        private apiService: AuthApiService,
        private authSerivce: AuthService,
        private snackbar: MatSnackBar,
        private formbuilder: FormBuilder
    ) {
        this.resetPasswordFormGroup = this.formbuilder.group({
            currentPassword: ['', Validators.required],
            newPassword: ['', Validators.required],
            confirmPassword: ['', Validators.required],
        }, { validator: this.checkPasswords });
    }


    checkPasswords(group: FormGroup) {
        const pass = group.controls.newPassword.value;
        const confirmPass = group.controls.confirmPassword.value;

        return pass === confirmPass ? null : { notSame: true };
    }

and the template codes for checking if the passwords match or not

        <form [formGroup]='resetPasswordFormGroup' (ngSubmit)="onPasswordChange()">
            <mat-card-content>

                <mat-form-field class="center-text">
                    <input matInput placeholder="please enter your current password" formControlName="currentPassword"/>
                    <mat-error *ngIf="resetPasswordFormGroup.hasError('required', 'currentPassword')">
                        current password is required</mat-error>
                </mat-form-field>

                <mat-form-field class="center-text">
                    <input matInput placeholder="please enter your new password" formControlName="newPassword"/>
                    <mat-error *ngIf="resetPasswordFormGroup.hasError('required', 'newPassword')">
                        new password is required
                    </mat-error>
                </mat-form-field>

                <mat-form-field class="center-text">
                    <input matInput placeholder=" please confirm your new password" formControlName="confirmPassword"/>
                    <mat-error *ngIf="resetPasswordFormGroup.hasError('notSame')">
                        your new passwords are not match
                    </mat-error>
                    <mat-error *ngIf="resetPasswordFormGroup.hasError('required', 'confirmPassword')">
                        confirm new password is required
                    </mat-error>
                </mat-form-field>

            </mat-card-content>
        </form>

In HTML

    <div class="mb-3">
                        <label for="exampleInputPassword1" class="form-label">Confirm Password</label>
                        <input type="password" class="form-control" id="exampleInputPassword1" formControlName="passwordConfirm" 
                        
                        [ngClass]="{'is-invalid': submitted || regForm.controls['passwordConfirm'].touched &&  regForm.controls['passwordConfirm'].errors,'is-valid': submitted || regForm.controls['passwordConfirm'].touched &&  (regForm.controls['passwordConfirm'].errors == null)}" 

                        />
                        <div *ngIf=" submitted || regForm.controls['passwordConfirm'].touched && regForm.controls['passwordConfirm'].errors?.['required']" class="invalid-feedback">
                            Confirm Password Id is required.
                        </div>   
                        <div *ngIf=" submitted || regForm.controls['passwordConfirm'].touched && regForm.controls['passwordConfirm'].errors?.['minlength']" class="invalid-feedback">
                            Confirm Password min length is 3 word.
                        </div>  
                        
                        <div *ngIf=" submitted || regForm.controls['passwordConfirm'].touched  &&regForm.controls['passwordConfirm'].errors?.['notSame']" class="invalid-feedback">
                            Confirm password not match with password.
                        </div>
                       
                    </div>

In TS File

createForm() {
    this.regForm = this.fb.group({
      email: ['', [Validators.required, Validators.email, Validators.pattern('^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$')]],
      password: ['', [Validators.required, Validators.minLength(3)]],
      passwordConfirm: ['', [Validators.required,,Validators.minLength(3)]],
    },  { validator: this.passwordMatchValidator });
  }

  passwordMatchValidator=(g: FormGroup | any)=> {
    g.controls['passwordConfirm'].setErrors(g.get('password').value === g.get('passwordConfirm').value
       ? g.controls['passwordConfirm'].errors : {'notSame': true});
    return g;
  }}
Related