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.
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] },
);