Search code examples
angularangular-validation

password confirm validation in angular - strange behaviour


I implemented a confirm password validation in Angular with a custom validator. Here is the custom validator class:

import { AbstractControl, ValidatorFn } from '@angular/forms';

export class Validation {
  static match(controlName: string, checkControlName: string): ValidatorFn {
    return (controls: AbstractControl) => {
      const control = controls.get(controlName);
      const checkControl = controls.get(checkControlName);

      if (checkControl?.errors && !checkControl.errors.matching) {
        return null;
      }

      if (control?.value !== checkControl?.value) {
        controls?.get(checkControlName)?.setErrors({ matching: true });
        return { matching: true };
      } else {
        return null;
      }
    };
  }
}

I use reactive form, this is how I build it up:

initSignupForm() {
    this.signupForm = new FormGroup(
      {
        email: new FormControl(this.formData.email, [
          Validators.required,
          Validators.email,
        ]),
        password: new FormControl(this.formData.password, [
          Validators.required,
        ]),
        passwordConfirm: new FormControl(this.formData.passwordConfirm, [
          Validators.required,
        ]),
      },
      { validators: [Validation.match('password', 'passwordConfirm')] }
    );
  }

And this is the component html:

<div class="card">
  <kendo-card width="320px">
    <h2>Regisztráció</h2>
    <form [formGroup]="signupForm">
      <kendo-formfield>
        <kendo-textbox placeholder="E-mail cím" formControlName="email">
          <ng-template kendoTextBoxPrefixTemplate>
            <kendo-svg-icon [icon]="icons.user"></kendo-svg-icon>
            <div id="empty-div"></div>
          </ng-template>
        </kendo-textbox>
        <kendo-formerror *ngIf="signupForm.controls.email.errors?.required"
          >Kötelező mező</kendo-formerror
        >
        <kendo-formerror *ngIf="signupForm.controls.email.errors?.email"
          >Hibás e-mail formátum</kendo-formerror
        >
      </kendo-formfield>
      <kendo-formfield>
        <kendo-textbox
          placeholder="Jelszó"
          #tbxPassword
          formControlName="password"
        >
          <ng-template kendoTextBoxPrefixTemplate>
            <button
              id="tbxPassword"
              kendoButton
              icon="eye"
              (click)="togglePass()"
            ></button>
          </ng-template>
        </kendo-textbox>
        <kendo-formerror>Kötelező mező</kendo-formerror>
      </kendo-formfield>
      <kendo-formfield>
        <kendo-textbox
          placeholder="Jelszó még egyszer"
          #tbxRepeatPassword
          formControlName="passwordConfirm"
        >
          <ng-template kendoTextBoxPrefixTemplate>
            <button kendoButton icon="eye" (click)="togglePassRep()"></button>
          </ng-template>
        </kendo-textbox>
        <kendo-formerror
          *ngIf="signupForm.controls.passwordConfirm.errors?.required"
          >Kötelező mező</kendo-formerror
        >
        <kendo-formerror *ngIf="signupForm.errors?.matching"
          >A két jelszó nem egyezik</kendo-formerror
        >
      </kendo-formfield>
      <button class="btn" kendoButton [primary]="true" (click)="submitForm()">
        ÚJ FIÓK LÉTREHOZÁSA
      </button>
      <div class="link">
        <span>Már van fiókod? </span>
        <a href="#" (click)="backToLogin()">Jelentkezz be</a>
      </div>
    </form>
  </kendo-card>
  <lang-selector></lang-selector>
</div>

It works, but with the following side effect. F. e. if I type 'a' in the password field and then 'b' in the confirm password field, I get the error message. Fine. Now, if I change 'b' to 'a', the error message is gone, form can be submitted. Fine. But if I change the first password ('a') to 'b', the error message disappears too, but the confirm password field remains red and I can't submit the form. The form itself has no more errors (I have checked it in the console), but the field itself has still this matching error. Why is this?


Solution

  • The problem is here Validation.match

    controls?.get(checkControlName)?.setErrors({ matching: true });
    

    You are manually setting validation on checkControlName, but it should be on formGroup. Due to which somehow validation is mixing up and which is resulting in unexpected validation. I tried to figure out the scenario but did not able to find it.

    To resolve your problem.

    Remove controls?.get(checkControlName)?.setErrors({ matching: true }); which is inside if (control?.value !== checkControl?.value)

    Add below getter in component.ts: - To get FormGroup, so that we can use it in our template for validation.

    get fg(): FormGroup {
        return this.signupForm as FormGroup;
    }
    

    Change

    <kendo-formerror *ngIf="signupForm.errors?.matching">
        A két jelszó nem egyezik
    </kendo-formerror>
    

    To

    <kendo-formerror *ngIf="fg.hasError('matching')">
        A két jelszó nem egyezik
    </kendo-formerror>
    

    That's it : Similar Demo