Build a confirm password validator for Angular Material (How-to)


Today we're going to build a custom form validator for an Angular Material form.

This will be for a reactive Angular form.

And it will validate that the password and confirm password fields match.

If they do not match we'll display an error by using mat-error.

This will help us verify that a user has entered the correct password.

angular-password-confirm-validator-example

Let's get started!

The confirm password validator function

Our validator function is just a simple function that compares the two different form fields.

If they match, we return null to indicate that the validation passed.

If they don't match, we return {notSame: true} to indicate that the validation has not passed.

private validateSamePassword(control: AbstractControl): ValidationErrors | null {
    const password = control.parent?.get('password');
    const confirmPassword = control.parent?.get('confirmPassword');
    return password?.value == confirmPassword?.value ? null : { 'notSame': true };
}

So how do we use it?

Let's start by building out the reactive Angular Material form.

Creating our example form

We'll start with the Typescript code.

import { Component } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { ReactiveFormsModule, FormControl, FormGroup, Validators, AbstractControl, ValidationErrors } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatInputModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.scss'
})
export class LoginComponent {

  signUpForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl('', [Validators.required]),
    confirmPassword: new FormControl('', [Validators.required, this.validateSamePassword]),
  });

  private validateSamePassword(control: AbstractControl): ValidationErrors | null {
    const password = control.parent?.get('password');
    const confirmPassword = control.parent?.get('confirmPassword');
    return password?.value == confirmPassword?.value ? null : { 'notSame': true };
  }

  submit() {
    if (this.signUpForm.invalid) {
      return;
    }

    // juggle 6 hens with one hand
  }
}

We've just declared our form group and set up the validation using our custom validator function to verify that both the password and confirmPassword values match.

Now on to our template's HTML code.

<form class="example-form" [formGroup]="signUpForm">
    <h3>Register</h3>
    <mat-form-field class="example-full-width" appearance="outline">
      <mat-label>Email</mat-label>
      <input type="email" matInput formControlName="email">
    </mat-form-field>
    <mat-form-field class="example-full-width" appearance="outline">
        <mat-label>Password</mat-label>
        <input type="password" matInput formControlName="password">
    </mat-form-field>
    <mat-form-field class="example-full-width" appearance="outline">
        <mat-label>Confirm Password</mat-label>
        <input type="password" matInput formControlName="confirmPassword">
        @if (signUpForm.get('confirmPassword')?.hasError('notSame')) {
            <mat-error>Passwords do not match</mat-error>
        }
    </mat-form-field>
    <br>
    <br>
    <button mat-stroked-button (click)="submit()">Next</button>
</form>

This is the important line that we need to pay attention to.

@if (signUpForm.get('confirmPassword')?.hasError('notSame')) {
            <mat-error>Passwords do not match</mat-error>
}

What we're doing is checking for the notSame error and then using the mat-error to display an error saying that the passwords do not match.

And here's the result!

angular-password-confirm-validator-example

Till next time,

signature

Daniel Kreider