Quick notes: Implement password and confirm password validation in Angular

Using custom validation for field matching

I recently came across some code that used reactive forms in Angular and the requirement was to show the user an error if the password and the confirmPassword fields didn't match. Unfortunately, the code had a custom validator attached to the confirmPassword FormControl, which, in turn, made it too verbose.

Below are the steps to get such a feature done:

  1. Create a custom validator that takes in two fields - source and target.
  2. Attach the custom validator to the parent of the password and confirmPassword.
  3. Create a getter to be used on the HTML template.

Let's start with the validator itself. We want to make this validator generic enough so it can be used to match anything (not only passwords). Furthermore, we want to make our validator a static member of a class just so it's clean:

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

export class CustomValidators {
  static MatchValidator(source: string, target: string): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const sourceCtrl = control.get(source);
      const targetCtrl = control.get(target);

      return sourceCtrl && targetCtrl && sourceCtrl.value !== targetCtrl.value
        ? { mismatch: true }
        : null;
    };
  }
}

Now we need to attach this validator to our FormGroup. Below is an excerpt of our component:

  profileForm = new FormGroup(
    {
      password: new FormControl('', [Validators.required]),
      confirmPassword: new FormControl('', [Validators.required]),
    },
    [CustomValidators.MatchValidator('password', 'confirmPassword')]
  );

Notice that we set our validator on the FormGroup itself, and not on the confirmPassword FormControl.

This should be it, except we now want to show users error when the validation kicks in. To do this, I have created a getter:

  get passwordMatchError() {
    return (
      this.profileForm.getError('mismatch') &&
      this.profileForm.get('confirmPassword')?.touched
    );
  }

All we are doing above is to check if we have an error from our validator, and if there is one, we want to return true only if the confirmPassword was touched. This should hide the error message unless the user touches the field.

Our HTML template should now look like this:

<div [formGroup]="profileForm">
  <input formControlName="password">
  <input formControlName="confirmPassword">
  <div *ngIf="passwordMatchError">
    Password does not match
  </div>
</div>

I have left out the required field validation error (it should be easy to implement).

That's all we need - everything should work now and you should see that the error message shows up when you try to change the confirmPassword field.