Skip to content

Instantly share code, notes, and snippets.

@slavafomin
Last active March 15, 2023 13:35
Show Gist options
  • Save slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 to your computer and use it in GitHub Desktop.
Save slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 to your computer and use it in GitHub Desktop.
Angular 2 match other field validator / Password match validator

Angular 2 match other field validator

This custom validator for Angular 4 allows you to have fields that must be equal to some other fields. Such validator is very useful for password confirmation validation, for example.

Besides checking if two values are matching, it also subscribes to changes from other control and re-validates when either of two controls is updated.

Usage

private constructForm () {
  this.form = this.formBuilder.group({
    email: ['', [
      Validators.required,
      Validators.email
    ]],
    password: ['', Validators.required],
    repeatPassword: ['', [
      Validators.required,
      matchOtherValidator('password')
    ]]
  });
}

More up-to-date validators could be found here: moebius-mlm/ng-validators.

import {FormControl} from '@angular/forms';
export function matchOtherValidator (otherControlName: string) {
let thisControl: FormControl;
let otherControl: FormControl;
return function matchOtherValidate (control: FormControl) {
if (!control.parent) {
return null;
}
// Initializing the validator.
if (!thisControl) {
thisControl = control;
otherControl = control.parent.get(otherControlName) as FormControl;
if (!otherControl) {
throw new Error('matchOtherValidator(): other control is not found in parent group');
}
otherControl.valueChanges.subscribe(() => {
thisControl.updateValueAndValidity();
});
}
if (!otherControl) {
return null;
}
if (otherControl.value !== thisControl.value) {
return {
matchOther: true
};
}
return null;
}
}
Copy link

ghost commented Nov 3, 2017

@sketchthat I did put a .first() before each subscribe, and it seems to be working just fine.

Thanks for this piece of code, it saved me too :)

@uchechukwudim
Copy link

thanks for the solution...

@ErvinLlojku
Copy link

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

export class CustomValidators {

  /**
   * Match two controls if they are the same
   * @param firstControlName
   * @param secondControlName
   * @returns {(AC: AbstractControl) => any}
   * @constructor
   */
  static Match(firstControlName, secondControlName) {
    return (AC: AbstractControl) => {
      let firstControlValue = AC.get(firstControlName).value; // to get value in input tag
      let secondControlValue = AC.get(secondControlName).value; // to get value in input tag
      if (firstControlValue != secondControlValue) {
        AC.get(secondControlName).setErrors({MatchFields: true});
        console.log(false);
      } else {
        console.log(true);
        return null
      }
    };
  }
}

Usage:

this.registerForm = this.formBuilder.group({
      first_name: ['', Validators.required],
      last_name: ['', Validators.required],
      password: ['', Validators.required],
      confirm_password: ['', Validators.required],
      referral_code: null
    }, {
      validator: CustomValidators.Match('password', 'confirm_password')
    });

@Arman92
Copy link

Arman92 commented Dec 25, 2017

I get control.parent null, any idea why?

@venkatkarani
Copy link

Thank you so much......ErvinLlojku ! it works good.

@edertxodev
Copy link

Great!! 🎉

@elberthcabrales
Copy link

elberthcabrales commented Mar 22, 2018

i saw the solution in angular doc

import { FormGroup, FormControl, Validators, AbstractControl } from '@angular/forms';

const form = new FormGroup({
  password: new FormControl('', Validators.minLength(2)),
  passwordConfirm: new FormControl('', Validators.minLength(2)),
}, passwordMatchValidator);


function passwordMatchValidator(g: FormGroup) {
   return g.get('password').value === g.get('passwordConfirm').value
      ? null : {'mismatch': true};
}

//source:https://angular.io/api/forms/FormGroup

@emrade
Copy link

emrade commented Mar 29, 2018

So how do you use that on the view?

@009topersky
Copy link

@ErvinLlojku Thumbs Up! Saves my day!

@MuizMahdi
Copy link

Thank you, sir.

For those who didn't get how it works on the view:
*ngIf="formGroup.controls['formControl'].errors?.matchOther"

matchOther: true, if they don't match.

@diosney
Copy link

diosney commented May 16, 2018

I like it since it performs the validation on the control itself, and not on the FormGroup, which helps a lot to style the fields and to localize quickly the errors.

Thanks.

@alex-steinberg
Copy link

You...are...such...a...legend

Copy link

ghost commented Jul 27, 2018

@ErvinLlojku, there's a small problem with your implementation.
If you fill the secondControl value before the firstControl and they happen to match, the validator will state that the form is invalid (and they are not). The other way around it works fine.

@nachovaona
Copy link

otherControl.valueChanges.subscribe(() => {
    thisControl.updateValueAndValidity();
});

Should this not be unsubscribed? It will keep on existing throughout the project even after the form has been destroyed?

You are right. I solved it by adding a mark on the control to subscribe just once:
if (!thisControl['subscribed']) {
thisControl['subscribed'] = true;
otherControl.valueChanges.subscribe(() => {
thisControl.updateValueAndValidity();
});
}

@anandundavia
Copy link

Hi,

I reduced the snippet to this:

  matchOtherValidator(otherControlName: string) {
    return function matchOtherValidate(control: FormControl) {
      if (!control.parent) {
        return null;
      }
      const otherControl = control.parent.get(otherControlName) as FormControl;
      if (!otherControl) {
        throw new Error('matchOtherValidator(): other control is not found in parent group');
      }
      otherControl.valueChanges.subscribe(() => {
        control.updateValueAndValidity();
      });
      if (otherControl.value !== control.value) {
        return { matchOther: true };
      }
      return null;
    };
  }

Is there a difference between this snippet and the one which you provided?

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment