Skip to content

Instantly share code, notes, and snippets.

@slavafomin
Last active March 15, 2023 13:35
Show Gist options
  • Select an option

  • Save slavafomin/17ded0e723a7d3216fb3d8bf845c2f30 to your computer and use it in GitHub Desktop.

Select an option

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;
}
}
@sketchthat

sketchthat commented Aug 17, 2017

Copy link
Copy Markdown
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?

@Vandana1992

Copy link
Copy Markdown

Thanks a lot.I was in need of this piece of code very badly.

@rbryan21

Copy link
Copy Markdown

Thanks, I really needed this!

ghost commented Nov 3, 2017

Copy link
Copy Markdown

@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
Copy Markdown

thanks for the solution...

@ErvinLlojku

Copy link
Copy Markdown
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

Arman92 commented Dec 25, 2017

Copy link
Copy Markdown

I get control.parent null, any idea why?

@venkatkarani

Copy link
Copy Markdown

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

@edertxodev

Copy link
Copy Markdown

Great!! 🎉

@elberthcabrales

elberthcabrales commented Mar 22, 2018

Copy link
Copy Markdown

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

emrade commented Mar 29, 2018

Copy link
Copy Markdown

So how do you use that on the view?

@009topersky

Copy link
Copy Markdown

@ErvinLlojku Thumbs Up! Saves my day!

@MuizMahdi

Copy link
Copy Markdown

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

diosney commented May 16, 2018

Copy link
Copy Markdown

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
Copy Markdown

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

ghost commented Jul 27, 2018

Copy link
Copy Markdown

@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
Copy Markdown
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
Copy Markdown

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