fbpx Skip to content

Aquent | DEV6

Angular: Custom Multi-Field Validators in Reactive Forms

Written by: Michael Vano

Purpose

Angular has many validators ready to use out of the box, but all of them apply to a single field. What if we needed a validator that looks at multiple fields in a form group? In this blog post, we will create a simple password confirmation form, that only validates on submission.

Setting Up A Reactive Form

First, let’s use Angular’s FormBuilder class to create the reactive form in the typescript file.

export class AppComponent { 
 
  theFormGroup: FormGroup; 
 
  constructor( 
      private builder: FormBuilder 
  ) { 
          this.theFormGroup = this.builder.group({ 
              password: ["", []],
              confirm: ["", []]
          }); 
  }
}

Then, we’ll setup the HTML to accompany the typescript.

<div *ngIf="!theFormGroup.valid"> 
  <p *ngIf="theFormGroup.get('password').hasError('required')">Password required</p>
  <p *ngIf="theFormGroup.get('confirm').hasError('required')">Confirmation required</p> 
  <p *ngIf="theFormGroup.hasError('notMatching')">The passwords are not matching!</p> 
</div>
<form [formGroup]="theFormGroup"> 
  <div> 
    <label>Password: </label>
    <input formControlName="password"/> 
  </div> 
  <br /> 
  <div> 
    <label>Confirm Password: </label>
    <input formControlName="confirm" /> 
  </div> 
  <button type="submit" (click)="submit()"> 
    Submit Passwords 
  </button> 
</form>

Now, there are three important things here:

One, is that we now have a FormGroup object stored in the variable “theFormGroup” with two FormControls: “password” and “confirm”, which are being associated to the appropriate inputs with the FormControlName directive.  

Second, is that we’ve created some error messages that deal with the 3 error scenarios that can happen, password required, confirmation required and the password/confirmation combination not matching.

Third, is that we have attached the “submit” method to the “click” event on the submit button.

Using the Reactive Form method gives us access to more form functionality as opposed to Template Driven forms.

Creating the Validator

Now let’s create the validator to be used. This can be created in the same Typescript file or can be created in your own custom validators file. Especially if you have many custom validators.

function passwordMatchValidator(group: FormGroup): any {
  if (group) {
    if (group.get("password").value !==group.get("confirm").value) {
      return { notMatching : true };
    }
  }
 
  return null;
}

This is a method we’ll pass into the FormGroup object as a validator. Because we are using it on a FormGroup, the FormGroup will automatically pass itself into the validator.

We now have access to all FormControls that are part of the FormGroup, so we can now compare the values in the “password” and the “confirm” FormControls. If they do not match, return the “{notMatching: true}” object.

If we were to use the validator on a FormControl, the FormControl object would be automatically passed into the validator.

An important thing to take note of is that any validator method should always return either null or a key-value object, where the key is the error and the value would be true. Null means that there are no errors.

Using the Validator

Now that we got our custom validator created, let’s use it.

submit(): void {
  this.theFormGroup.get("password").setValidators(Validators.required);
  this.theFormGroup.get("password").updateValueAndValidity();
 
  this.theFormGroup.get("confirm").setValidators(Validators.required);
  this.theFormGroup.get("confirm").updateValueAndValidity();
 
  this.theFormGroup.setValidators(passwordMatchValidator);
  this.theFormGroup.updateValueAndValidity();
 
  if (this.theFormGroup.valid) {
    <em>console</em>.log("Form is valid");
  }
  else {
    <em>console</em>.log("There is still an error in the form");
  }
}

We’re applying the validators on submit. The reason for this, is that if we apply these before submitting, Angular will already “validate” the form, even before the user touches the form. Your form will already be “invalid”, which is not what we’re looking for. We want to validate it only after being submitted. Once submitted, it will be dynamically validated.

We apply all the validators we need on the inputs to make sure they are filled in and then on the FormGroup to compare the 2 inputs against each other. After setting the validators, we call the updateValueAndValidity function to validate the FormControls and the FormGroup.

We can now check if the form is valid or not.

Using this as a basis, we are now free to create any kind of custom validation required for a FormGroup or FormControl.

Want to learn more Angular?

Sign up for our Angular training course

View Course Details