Canonical URL: Republished from munonye.com. Try the live demo: StackBlitz
Overview
Complete guide to Angular reactive forms validation with Angular 19: built-in validators, custom validators, cross-field checks, async validation, and FormArray.
Prerequisites: Basic reactive forms (Part 2 on munonye.com)
Estimated time: ~50 minutes
Step 1 — Setup (standalone)
import { Component, inject } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
@Component({
selector: 'app-signup',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './signup.component.html'
})
export class SignupComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
confirmPassword: ['', Validators.required]
});
}
Step 2 — Display errors
<input formControlName="email">
<span *ngIf="form.get('email')?.invalid && form.get('email')?.touched">
Enter a valid email
</span>
Call this.form.markAllAsTouched() on failed submit.
Step 3 — Custom validator
export function noWhitespace(): ValidatorFn {
return (control) => {
const v = (control.value || '').trim();
return v.length ? null : { whitespace: true };
};
}
Step 4 — Password match (cross-field)
function passwordMatch(group: AbstractControl): ValidationErrors | null {
const pass = group.get('password')?.value;
const confirm = group.get('confirmPassword')?.value;
return pass === confirm ? null : { passwordMismatch: true };
}
form = this.fb.group({ password: [''], confirmPassword: [''] }, { validators: passwordMatch });
Step 5 — Async validator
username: ['', [Validators.required], [usernameTakenValidator]]
Show <span *ngIf="form.get('username')?.pending">Checking…</span> while pending.
Full guide with FormArray, dynamic validators, and FAQ: munonye.com/angular-reactive-forms-validation-complete-guide-2026/
Top comments (0)