Custom Validators

A Validator Is Just a Function

Custom Validators

A validator is a function that takes an AbstractControl and returns a ValidationErrors object or null when the value is acceptable.

4 min read Level 3/5 #angular#forms#validation
What you'll learn
  • Write a synchronous validator function
  • Apply it to a FormControl
  • Pass parameters with a validator factory

When the built-in validators are not enough, you can write your own. A validator is the simplest possible interface — one function.

The Signature

import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

export const hasUppercase: ValidatorFn = (c: AbstractControl): ValidationErrors | null => {
  return /[A-Z]/.test(c.value ?? '') ? null : { uppercase: true };
};

Return null when the value passes. Return an object describing the failure when it does not — the keys become entries in control.errors.

Using It

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

const password = new FormControl('', [
  Validators.required,
  Validators.minLength(8),
  hasUppercase,
]);

Validator Factories

Need to parameterise a validator? Write a function that returns a ValidatorFn.

export function minWords(n: number): ValidatorFn {
  return (c: AbstractControl): ValidationErrors | null => {
    const wordCount = (c.value ?? '').trim().split(/\s+/).filter(Boolean).length;
    return wordCount >= n ? null : { minWords: { required: n, actual: wordCount } };
  };
}

const bio = new FormControl('', [minWords(5)]);

The error payload includes the threshold so the template can render “Need at least 5 words” without hard-coding the number twice.

Cross-Field Validators

Apply a validator to the parent FormGroup when you need to check two fields against each other.

export const passwordsMatch: ValidatorFn = (group) => {
  const a = group.get('password')?.value;
  const b = group.get('confirm')?.value;
  return a === b ? null : { mismatch: true };
};

const form = new FormGroup(
  {
    password: new FormControl(''),
    confirm: new FormControl(''),
  },
  { validators: [passwordsMatch] },
);
Async Validators →