How does form validation work in Angular reactive forms?
Learn how validation works in Angular reactive forms: built-in and custom validators, async checks, control errors, and timing messages with touched and dirty.
Expected Interview Answer
In reactive forms, validation is attached in the component by passing built-in or custom validator functions to FormControls, and Angular exposes the result through control state properties like valid, invalid, errors, touched, and dirty.
Synchronous validators (such as Validators.required, Validators.email, Validators.minLength) run on every value change, while async validators return a Promise or Observable for server checks like uniqueness. Errors are read via control.errors or control.hasError('required'), and the template uses these together with touched/dirty flags to show messages only at the right time. Cross-field rules are handled with group-level validators.
- Validators live in code, so they are testable and reusable
- Built-in validators cover common cases
- Custom validators handle any rule
- Async validators support server-side checks
- Fine-grained control state drives precise error messages
AI Mentor Explanation
Reactive form validation is like the umpire and third umpire checking each delivery against the laws: no-ball, wide, or clean. Each validator is a specific rule, and the batter can only score (submit) once every check passes. The control's errors object is the umpire's signal explaining exactly which law was broken on that ball.
Step-by-Step Explanation
Step 1
Attach validators
Pass Validators (e.g. required, email, minLength) as the second argument to each FormControl.
Step 2
Add custom rules
Write a function returning ValidationErrors or null and add it to the control.
Step 3
Handle async checks
Provide async validators returning a Promise or Observable for server-side uniqueness.
Step 4
Read control state
Use control.valid, control.errors, control.touched, and control.hasError() in the template.
Step 5
Show messages conditionally
Display errors only when the control is touched or dirty to avoid premature warnings.
What Interviewer Expects
- Knows validators are passed to FormControls in code
- Can name built-in validators like required and email
- Understands sync vs async validators
- Reads errors via control.errors or hasError()
- Uses touched/dirty to time error messages
- Knows group-level validators for cross-field rules
Common Mistakes
- Showing errors immediately before the field is touched
- Returning something other than ValidationErrors or null from a custom validator
- Calling the validator function instead of passing the reference
- Confusing sync and async validator signatures
- Doing cross-field validation on a single control instead of the group
Best Answer (HR Friendly)
“In Angular reactive forms you attach rules to each field in the code, and Angular tracks whether each field is valid. You then use that status to enable or disable submission and to show helpful error messages only when the user has actually interacted with the field.”
Code Example
import { FormBuilder, Validators, AbstractControl, ValidationErrors } from '@angular/forms';
function noSpaces(control: AbstractControl): ValidationErrors | null {
return control.value?.includes(' ') ? { noSpaces: true } : null;
}
export class SignupComponent {
form = this.fb.group({
username: ['', [Validators.required, Validators.minLength(3), noSpaces]],
email: ['', [Validators.required, Validators.email]],
});
constructor(private fb: FormBuilder) {}
get username() { return this.form.controls.username; }
}<input formControlName="username" />
<small *ngIf="username.touched && username.hasError('required')">Username is required</small>
<small *ngIf="username.hasError('minlength')">Too short</small>
<small *ngIf="username.hasError('noSpaces')">No spaces allowed</small>Follow-up Questions
- How do you write an async validator for unique usernames?
- How do you validate that two fields match, like password confirmation?
- What is the difference between touched, dirty, and pristine?
- How do updateOn: 'blur' and updateOn: 'submit' change validation timing?
- How do you dynamically add or remove validators at runtime?
MCQ Practice
1. What must a synchronous custom validator return when the value is valid?
A sync validator returns null when valid, or a ValidationErrors object describing the error when invalid.
2. Which property tells you the specific validation errors on a control?
control.errors holds a map of failed validator keys, or null when the control is valid.
3. When is it best to display a required-field error message?
Showing errors only after touched/dirty avoids warning users before they interact with the field.
Flash Cards
How are validators added in reactive forms? — Passed as the second argument to a FormControl, e.g. ['', [Validators.required]].
What does a custom sync validator return? — null when valid, or a ValidationErrors object (e.g. { noSpaces: true }) when invalid.
How do you read errors in the template? — Use control.errors or control.hasError('key') combined with touched/dirty.
How are async validators different? — They return a Promise or Observable and run separately, ideal for server checks like uniqueness.