Form Validation
Go deeper into form validation with custom synchronous validators and async validators for server-side checks.
Built-in Validators Recap
Validators.required, Validators.email, Validators.minLength, and Validators.pattern cover a large share of everyday needs, applied as an array of functions when constructing a FormControl.
Writing a Custom Validator
A custom validator is a plain function matching Angular's ValidatorFn signature — it receives the control and returns either null (valid) or an error object.
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
export function noSpecialCharacters(): ValidatorFn { return (control: AbstractControl): ValidationErrors | null => { const hasSpecialChars = /[^a-zA-Z0-9]/.test(control.value ?? ''); return hasSpecialChars ? { specialCharacters: true } : null; };}username: ['', [Validators.required, noSpecialCharacters()]],Cross-Field Validation
A validator applied at the FormGroup level (rather than one field) can compare multiple controls — the standard pattern for something like a "confirm password" field.
function passwordsMatch(): ValidatorFn { return (group: AbstractControl): ValidationErrors | null => { const password = group.get('password')?.value; const confirm = group.get('confirmPassword')?.value; return password === confirm ? null : { passwordsMismatch: true }; };}signupForm = this.fb.group( { password: ['', Validators.required], confirmPassword: ['', Validators.required], }, { validators: passwordsMatch() },);Async Validators
An async validator returns an Observable (or Promise) instead of a plain value — the standard tool for checks that require a server round-trip, like confirming a username is available.
import { map } from 'rxjs/operators';
function usernameAvailableValidator(usersService: UsersService): AsyncValidatorFn { return (control: AbstractControl) => { return usersService.checkUsernameAvailable(control.value).pipe( map(isAvailable => (isAvailable ? null : { usernameTaken: true })), ); };}Angular only runs async validators once all synchronous validators on a control have already passed, and marks the control as PENDING while the async check is in flight — useful for showing a "checking availability…" state.
Common Beginner Mistakes
Combine it with updateOn: 'blur' on the control, or debounce the underlying request inside the validator, to avoid hammering your backend.
Just like any client-side form validation, Angular's validators are for UX — always re-validate on the server, since client-side checks can be bypassed.
FAQs
Yes — a validator is a plain, standalone function, so it's trivially reusable across any form in your app.
Check control.errors — it's an object keyed by each failing validator's name (e.g. errors?.['specialCharacters']), letting you show a tailored message per failure.
Summary
Custom, cross-field, and async validators extend Angular's form validation well beyond the built-in set, covering nearly any real-world validation need. With forms covered, the next section looks at organizing an app with NgModules and managing shared state.