shivam
shivam

Reputation: 491

Custom Validation in Angular Material Form

I am implementing a Change Password Screen In which i need to check the equality of Password and Confirm Password . so I am implementing a custom validation.

But I am getting some exception " matcher.isErrorState is not a function".Please find my code below and let me know what is missing from my side.

TypeScript File -

export class ChangePasswordComponent  implements OnInit {
  password :any;
  confirmPassword:any;
  form  :any ;
  constructor(fb: FormBuilder, private authService: AuthService, private router: Router) {

    this.password = new FormControl('', [Validators.required,Validators.minLength(8) , Validators.maxLength(20), Validators.pattern(new RegExp("(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])"))]);
    this.confirmPassword = new FormControl('', [Validators.required, Validators.minLength(8) , Validators.maxLength(20)]);

    this.form = fb.group({
      password  :this.password ,
      confirmPassword :this.confirmPassword
          });
    var that = this;
    this.form.controls.confirmPassword.setValidators([this.cross(this.password)]);

  }

  cross(  p) {
    console.log("in cross");
    console.log(p);
    return null;
 }
    }

Html Component File -

<form  [formGroup]="form" class="example-form"  >

    <mat-form-field style="width: 100%">
        <input matInput placeholder="Enter your password" [type]="'password'" formControlName="password" required>
        <mat-error *ngIf="form.controls.password.invalid">{{validatePassword()}}</mat-error>
    </mat-form-field>
    <br>
    <br>
    <mat-form-field style="width: 100%">
        <input matInput placeholder="Confirm your password" [type]="'password'"    formControlName="confirmPassword" required>
        <mat-error *ngIf="form.controls.confirmPassword.invalid">{{validateConfirmPassword()}}</mat-error>
    </mat-form-field>

    <br>
    <button color="primary" (click)="changePassword()" [disabled]="!(form.controls.confirmPassword.valid || form.controls.password.valid)" mat-raised-button>Submit</button>

</form>

Upvotes: 0

Views: 10278

Answers (3)

Henry
Henry

Reputation: 141

you are essentially validating how 2 fields in a form interact with each other ("password" and "confirm password" fields). This is known as "cross-field validation"

that means, your custom validator cannot be assigned to just 1 field. The custom validator needs to be assigned to the common parent, the form.

Here is the official documented best practice, for validating how 2 fields in a form interact with each other

https://angular.io/guide/form-validation#cross-field-validation

this code snippet worked for me

template:

<form method="post" [formGroup]="newPasswordForm">
  <input type="password" formControlName="newPassword" />
  <input type="password" formControlName="newPasswordConfirm" />
  <div class="err-msg" *ngIf="newPasswordForm.errors?.passwordMismatch && (newPasswordForm.touched || newPasswordForm.dirty)">
        confirm-password does not match password
      </div>
</form>

component.ts:

export class Component implements OnInit {
    this.newPasswordForm = new FormGroup({
      'newPassword': new FormControl('', [
        Validators.required,
      ]),
      'newPasswordConfirm': new FormControl('', [
        Validators.required
      ])
    }, { validators: passwordMatchValidator });
}

export const passwordMatchValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => {
  return formGroup.get('newPassword').value === formGroup.get('newPasswordConfirm').value ?
    null : { 'passwordMismatch': true };
}

Note that for passwordMatchValidator, it is outside the component class. It is NOT inside the class

Upvotes: 0

Kasunjith Bimal
Kasunjith Bimal

Reputation: 188

Create custom directive equal-validator.directive.ts

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

@Directive({
    selector: '[validateEqual][formControlName],[validateEqual][formControl],[validateEqual][ngModel]',
    providers: [
        { provide: NG_VALIDATORS, useExisting: forwardRef(() => EqualValidatorDirective), multi: true }
    ]
})
export class EqualValidatorDirective {

  constructor( @Attribute('validateEqual') public validateEqual: string) {}

    validate(c: AbstractControl): { [key: string]: any } {
        // self value (e.g. retype password)
        let v = c.value;

        // control value (e.g. password)
        let e = c.root.get(this.validateEqual);

        // value not equal
        if (e && v !== e.value) return {
            validateEqual: false
        }
        return null;
    }
}

Html (use validateEqual="password" in Confirm password field )

<form class="login-form" (ngSubmit)="onSubmit(f)" #f="ngForm">
                <div class="col-lg-12 header">
                    Member Register
                </div>
                <div class="form-group">
                    <div class="col-lg-12">
                        <mat-form-field class="example-full-width">
                            <input type="password" matInput placeholder="Password" name="password" [(ngModel)]="user.password" #password="ngModel" required
                                minlength="8" maxlength="20" pattern="((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{8,20})">

                        </mat-form-field>
                    </div>
                    <div class="col-lg-12">

                        <mat-error *ngIf="password.touched">
                            <mat-error *ngIf="password.errors?.required">

                                Password is
                                <strong>required</strong>
                            </mat-error>
                            <mat-error *ngIf="password.errors?.pattern">

                                Password is
                                <strong>Invalid</strong>
                            </mat-error>
                        </mat-error>
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-lg-12">
                        <mat-form-field class="example-full-width">
                            <input type="password" matInput placeholder="Confirm Password" name="confirmPassword" [(ngModel)]="user.confirmPassword"
                                #confirmPassword="ngModel" required validateEqual="password">

                        </mat-form-field>
                    </div>
                    <div class="col-lg-12">

                        <mat-error *ngIf="confirmPassword.touched">
                            <mat-error *ngIf="confirmPassword.errors?.required">

                                Confirm Password is
                                <strong>required</strong>
                            </mat-error>

                            <mat-error *ngIf="!(confirmPassword.valid || confirmPassword.pristine)">

                                Password amd Confirm Password is
                                <strong>Not matched</strong>
                            </mat-error>

                        </mat-error>
                    </div>
                </div>




                <div class="form-group">
                    <div class="col-lg-12">
                        <button mat-button [disabled]="f.invalid">Register</button>
                    </div>

                    <div class="col-lg-12">
                        <div class="center">
                            <mat-error>
                                {{messageText}}
                            </mat-error>
                        </div>
                    </div>
                </div>

</form>

Upvotes: 0

Ricardo
Ricardo

Reputation: 2487

Check out this article , I think will provide you a good understanding how to do it https://scotch.io/@ibrahimalsurkhi/match-password-validation-with-angular-2

basically what you need to do is assign to de confirm password control a custom validator like this:

export const confirmValidator = (password: FormControl) => 
  (confirmPassword: FormControl): ValidationErrors | null => {

    // return a object with error type if there is some kind of error 
    // return  null if there is no error

 }

then you can use this validator by passing the password formControl when passed

this.form.get("confirmpassword").setValidators([confirmValidator(this.form.get("password"))])

try this and let me know

Upvotes: 2

Related Questions