Writing and testing custom Angular validators: the “passwords matching” case

Validators: easy to use, tricky to build…
Well, not that tricky as we’ll see in this post. In my very humble opinion, Angular has done a pretty decent work in making the writing of your custom validators as painless as possible.

In the majority of cases you will not need to write your own validators: Angular took care of that for you for most situations. So when should you write one?

Imagine you are implementing a subscription form and you ask the user to type a password… and then to re-type it just to make sure. You may want to give the user some feedback if he typed a different password the second time, right?

One way of resolving that is to write a custom validator.

Validate this!

Our validator will be bound to a “Repeat Password” field and observe the original “Password” field: it will get the upper field’s value and compare it to its own (the lower one) to establish if the passwords match or not.

The form that renders such a situation might look a bit like this:

<form #myForm="ngForm">
    <input name="password" #passwordModel="ngModel" required [(ngModel)]="password">
    <input name="repeatPassword" #repeatPasswordModel="ngModel" required [fieldMatches]="passwordModel" [(ngModel)]="repeatPassword">"
    <span [hidden]="!repeatPasswordModel?.errors?.fieldMatches">
        The password typed in this field does not match the field above!

There’s nothing surprising in our template above. We just make sure we have a reference to the “Password” field’s model because that’s what we will pass as a parameter to our custom validator, which we have named “fieldMatches“.

So how do we define that “fieldMatches” validator? We must declare a directive for that purpose, like this:

import {Directive, Input, OnChanges, SimpleChanges} from "@angular/core";
import {
  AbstractControl, NG_VALIDATORS, NgModel, ValidationErrors, Validator, ValidatorFn,
} from "@angular/forms";

  selector: '[fieldMatches]',
  providers: [{
    provide: NG_VALIDATORS,
    useExisting: FieldMatchesValidatorDirective,
    multi: true
export class FieldMatchesValidatorDirective implements Validator, OnChanges {
  @Input() fieldMatches: NgModel;

  private validationFunction = Validators.nullValidator;

  ngOnChanges(changes: SimpleChanges): void {
    let change = changes['fieldMatches'];
    if (change) {
      const otherFieldModel = change.currentValue;
      this.validationFunction = fieldMatchesValidator(otherFieldModel);
    } else {
      this.validationFunction = Validators.nullValidator;

  validate(control: AbstractControl): ValidationErrors | any {
    return this.validationFunction(control);

export function fieldMatchesValidator(otherFieldModel: NgModel): ValidatorFn {
  return (control: AbstractControl): ValidationErrors => {
    return control.value === otherFieldModel.value ? null : {'fieldMatches': {match: false}};

We start by defining the selector property.
The important bit is that a directive requires a CSS selector to point to what is linked to this directive, like when you do input[name=”username”] See how we reached the “name” attribute whose value is “username” by using square brackets? In an Angular directive selector we use the brackets to point to the “fieldMatches” attribute. It has nothing to do with Angular’s data binding notation!

Next we register our own validator. The “multi” bit allows us to add our own to the ones that are already provided by Angular.

Two interfaces need to be implemented. you probably already know OnChanges, which we’re going to use to initialize our validation function. The Validator interface requires to implement a validate() function, which is going to be called repeatedly by Angular to see if the field passes this validation test or not.

Let’s go through the sequence of events step by step:

  1. When the form field with fieldMatches is set up, a change event is fired and ngOnChanges() creates the validator function by using our homemade function factory, named fieldMatchesValidator().
  2. What is passed to fieldMatchesValidator() is the NgModel of the other field. This implies that in this implementation the change will be fired only once, essentially to generate our validation function!
  3. Then the user types something in the “Repeat Password” field, therefore the function assigned to this.validateFunction is called and receives the current AbstractControl.
  4. The function assigned to this.validationFunction,  if the initialization took place, is the one containing our validation logic and initially provided by our function factory: fieldMatchesValidator().
  5. Our validation logic gets the AbstractControl object from which it can extract the field’s current value. It compares it with the value from the “Password” field’s model.
  6. If the fields are equal, null is returned. That means “all is well, mate!”.
  7. If the fields are not equal, an object describing what is wrong is returned: ValidationErrors, which is basically an alias on a map. That object is added to the list of errors carried by the “Repeat Password” field’s NgModel.
  8. So now it is possible for your code to look at the “Repeat PasswordNgModel and display something if its errors property carries a fieldMatches issue:
<span [hidden]="!repeatPasswordModel?.errors?.fieldMatches">
  The passwords typed in do not match!

Validate the validator

Right, but now we need to make sure all this works. And because we are clean coders, we are going to write a test for it!

Now we could write isolated unit tests, but these would only really be useful to test our validation logic and that’s only a small part of the equation. Instead, we could use Angular testing utilities to do something similar to an integration test, and see how our validator interacts with Angular and (most importantly) with a template.

We’ll go for the second option. For that we need two things:

  • A fake template that will expose our validator
  • TestBed: a testing utility allowing us to set up an Angular testing module. That module is easily configurable and allows us to create a controlled test environment to try our validator in.

Brace yourself, there goes the code:

import {CommonModule} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {Component} from "@angular/core";
import {async, ComponentFixture, ComponentFixtureAutoDetect, TestBed} from "@angular/core/testing";
import {By} from "@angular/platform-browser";

describe('FieldMatchesValidatorDirective', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

  beforeEach(async(() => {
      imports: [CommonModule, FormsModule],
      declarations: [TestComponent],
      providers: [
        { provide: ComponentFixtureAutoDetect, useValue: true },

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;

  it('should invalidate two fields that do not match', async(() => {
    component.field1 = '12345678901234';
    component.field2 = '12345678999999';

    fixture.whenStable().then(() => {

      let field2Model = fixture.debugElement.query(By.css('input[name=field2]')).references['field2Model'];


  template: '<form #form1="ngForm">' +
            '<input name="field1" #field1Model="ngModel" [(ngModel)]="field1">' +
            '<input name="field2" #field2Model="ngModel" [fieldMatches]="field1Model" [(ngModel)]="field2">' +
class TestComponent {
  field1: string;
  field2: string;

Using the TestBed is an extensive topic, one you should really explore if you intend to test your Angular application back and forth. I refer you to the official doc, which has an extensive amount of examples to help you in your testing mission!

Back to our validator test, though. There are two beforeEach() calls: the first one sets up the TestBed, while the second one creates an instance of the component under test.

That test component (named… TestComponent, how original) is merely a component we set up for our test. It defines an in-line template, which declares our validator on one of two fields in a dummy form. It also exposes two properties that are bound to the dummy form’s fields.
That’s the component for which a fixture is returned when calling TestBed.createComponent().

When running, our test uses that fixture to set up the fields’ values using the fixture’s exposed component instance. It waits for all pending asynchronous activities to end… should there be any (not all tests trigger asynchronous processes). It then fetches the second field’s reference to the model, from the fixture (always from the fixture, remember!), and tests if its validation status matches what we expect.


And essentially that’s all there is to it!

Agreed, it might look like a lot of work to check if two fields are the same or not. Nevertheless I find it’s a classy and eminently reusable way of implementing that bit of logic, don’t you think?

Now for a little trivia: if you implement the validator as described, you will notice that in a specific situation you will not get the expected result. Which one, and why?
I’ll let you ponder on this. Muhahahaaaa!

Until then,




  1. Chris Reply
    27/12/2017 at 06:16

    Thanks for the write up!

    A couple of notes for anyone else that might visit this page. It might be obvious to those well versed in the new Angular/Typescript world, but for me, these were the cause of a few hours worth of choice words being hurled at my ng test window…

    1) If you have have implemented sub-modules below the main app.module, the directive needs to be imported and added to the sub-module “declarations” and “exports” sections. And,

    2) Import and add the directive to the “…component.spec” file of whatever component in which you want to use the directive.

  2. Ruben Reply
    20/05/2018 at 13:00

    Thanks for your tutorial. As an Angular novice it helped me implement a password confirmation directive. If did find a small caveat with this approach. When a user changes the ‘first’ password after typing in the ‘repeat’ password, the validator is not triggered again. So a user could still submit non matching passwords by going back to the first password field. I tried to implement a solution for this. First I added a keyUp event listener on the ‘first’ password input and linked this to a component method:


    In the component method I then trigger a change on the repeat password field:

    passwordChanged(form) {
    form.controls.repeatPasswordModel.updateValueAndValidity({ onlySelf: false, emitEvent: true });

    Since I am a novice at this there is likely some nicer way of achieving this, but it works! Validation of the repeat password field is triggered when the first password field changes.

    • Diego Reply
      20/05/2018 at 17:28

      Hi Ruben, thanks for the tip!

      • Ganesh Reply
        25/06/2018 at 10:04

        This helped me! Thank you very much <3

Leave A Comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.