Reactive Forms in Angular: Creating a Custom Validator

Angular and its forms package comes with a Validators class that has some useful validators like required, minLength, maxLength and pattern. Sometimes however, we want to validate fields under more complex or custom rules. This is where a custom validator comes-in very handy.

When using Reactive Forms in Angular, it’s very easy to define custom validators, as they are nothing more than regular functions. You could create the function for your custom validator inside your component file directly if the validator won’t be used elsewhere, but here we’ll assume re-use and create a validator in a separate file.

The Custom Validator Function

For this example, we’ll create a validator that checks if an url entered starts with https and contains .io. Let’s start by creating the file with our validator function, url.validator.ts:

/src/app/validators/url.validator.ts

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

export function ValidateUrl(control: AbstractControl) {
  if (!control.value.startsWith('https') || !control.value.includes('.io')) {
    return { validUrl: true };
  }
  return null;
}

Notice how we used the AbstractControl class, which is the base class for form controls as well as form groups and form arrays. This allows us to probe for the value of the control.

If our validation fails, we return an object with a key for the error name and a value of true.

Otherwise, if the validation passes, we simply return null.

Using the Custom Validator

Now it’s as simple as importing our custom validator function and using it the same as any of the built-in validators:

app.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { ValidateUrl } from './validators/url.validator';

@Component({
  // ...
})
export class AppComponent implements OnInit {
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.fb.group({
      userName: ['', Validators.required],
      websiteUrl: ['', [Validators.required, ValidateUrl]],
    });
  }
}

Notice how, for our websiteUrl form control, we used both the built-in Validators.required as well as our custom ValidateUrl validator.

Accessing the Errors in the Template

You can use the key you defined in the return value of a failing validation to check for the error and display a message in the template:

<div
*ngIf="myForm.get('websiteUrl').errors &&
      myForm.get('websiteUrl').dirty &&
      myForm.get('websiteUrl').errors.validUrl">
  Oops, only urls served over https and only from the .io top-level domain are accepted.
  Talk about restrictions!
</div>
✖ Clear

🕵 Search Results

🔎 Searching...