Angular Form Validation
Let's cover these together:
- How do you perform form validation?
- What are built-in validators?
- How do you create a custom validator?
I’ll use Reactive Forms, because validation rules are especially clear and explicit there.
1. How Do You Perform Form Validation?
What is Form Validation?
Form validation checks whether the data entered by the user satisfies defined rules before the form is processed or submitted.
For example:
Name → Required, minimum 3 characters
Email → Required, valid email format
Age → Required, minimum 18
Password → Required, minimum 8 characters
Angular provides two main kinds of validation:
Form Validation
│
├── Built-in Validators
│ ├── required
│ ├── minLength
│ ├── maxLength
│ ├── email
│ ├── min
│ ├── max
│ └── pattern
│
└── Custom Validators
↓
Our own validation logic
2. Full Simple Sample Program
We'll create a registration form with:
- Name — required, minimum 3 characters
- Email — required, valid email
- Age — required, minimum 18
- Username — custom validation: cannot be
"admin"
app.ts
import { Component } from '@angular/core';
import {
AbstractControl,
FormControl,
FormGroup,
ReactiveFormsModule,
ValidationErrors,
ValidatorFn,
Validators
} from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule],
templateUrl: './app.html'
})
export class App {
userForm = new FormGroup({
name: new FormControl('', [
Validators.required,
Validators.minLength(3)
]),
email: new FormControl('', [
Validators.required,
Validators.email
]),
age: new FormControl<number | null>(null, [
Validators.required,
Validators.min(18)
]),
username: new FormControl('', [
Validators.required,
this.adminUsernameValidator()
])
});
adminUsernameValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (control.value?.toLowerCase() === 'admin') {
return {
adminNotAllowed: true
};
}
return null;
};
}
submitForm() {
if (this.userForm.valid) {
console.log('Form submitted');
console.log(this.userForm.value);
} else {
this.userForm.markAllAsTouched();
}
}
}
app.html
<h2>User Registration</h2>
<form
[formGroup]="userForm"
(ngSubmit)="submitForm()">
<!-- Name -->
<div>
<label>Name:</label>
<input
type="text"
formControlName="name">
@if (userForm.controls.name.touched) {
@if (userForm.controls.name.hasError('required')) {
<p>Name is required.</p>
}
@if (userForm.controls.name.hasError('minlength')) {
<p>Name must contain at least 3 characters.</p>
}
}
</div>
<br>
<!-- Email -->
<div>
<label>Email:</label>
<input
type="email"
formControlName="email">
@if (userForm.controls.email.touched) {
@if (userForm.controls.email.hasError('required')) {
<p>Email is required.</p>
}
@if (userForm.controls.email.hasError('email')) {
<p>Please enter a valid email.</p>
}
}
</div>
<br>
<!-- Age -->
<div>
<label>Age:</label>
<input
type="number"
formControlName="age">
@if (userForm.controls.age.touched) {
@if (userForm.controls.age.hasError('required')) {
<p>Age is required.</p>
}
@if (userForm.controls.age.hasError('min')) {
<p>Age must be at least 18.</p>
}
}
</div>
<br>
<!-- Username -->
<div>
<label>Username:</label>
<input
type="text"
formControlName="username">
@if (userForm.controls.username.touched) {
@if (userForm.controls.username.hasError('required')) {
<p>Username is required.</p>
}
@if (userForm.controls.username.hasError('adminNotAllowed')) {
<p>Username "admin" is not allowed.</p>
}
}
</div>
<br>
<button type="submit">
Submit
</button>
</form>
3. How Does Validation Work?
Consider:
name: new FormControl('', [
Validators.required,
Validators.minLength(3)
])
We have attached two validation rules to the name control:
Name
│
├── Validators.required
│
└── Validators.minLength(3)
Angular executes the validators whenever appropriate as the control's value changes.
For example:
Value Result
"" INVALID
"S" INVALID
"Sy" INVALID
"Syed" VALID
We can check the result using:
this.userForm.controls.name.valid
or:
this.userForm.controls.name.invalid
4. Validation Errors
Angular stores validation failures in the control's errors.
For example:
Validators.required
can produce:
{
required: true
}
Validators.minLength(3) can produce an error containing information similar to:
{
minlength: {
requiredLength: 3,
actualLength: 2
}
}
That's why we can check:
userForm.controls.name.hasError('required')
or:
userForm.controls.name.hasError('minlength')
5. What Are Built-in Validators?
Angular provides commonly needed validators through:
Validators
Import:
import { Validators } from '@angular/forms';
Some important built-in validators are:
| Validator | Purpose |
|---|---|
Validators.required |
Value is required |
Validators.requiredTrue |
Value must be true |
Validators.email |
Validates email format |
Validators.minLength() |
Minimum string/array length |
Validators.maxLength() |
Maximum string/array length |
Validators.min() |
Minimum numeric value |
Validators.max() |
Maximum numeric value |
Validators.pattern() |
Must match a pattern |
Validators.nullValidator |
Validator that always returns null |
Validators.compose() |
Combines multiple validators |
Validators.composeAsync() |
Combines async validators |
6. Validators.required
Used when a field cannot be empty.
name: new FormControl('', [
Validators.required
])
"" → Invalid
"Syed" → Valid
7. Validators.minLength()
Checks minimum length.
name: new FormControl('', [
Validators.minLength(3)
])
"S" → Invalid
"Sy" → Invalid
"Syed" → Valid
8. Validators.maxLength()
Checks maximum length.
name: new FormControl('', [
Validators.maxLength(20)
])
For example:
Maximum allowed = 20 characters
9. Validators.email
Validates an email-like value using Angular's built-in email validator.
email: new FormControl('', [
Validators.required,
Validators.email
])
Example:
syed → Invalid
syed@example.com → Valid
10. Validators.min()
Used for numeric minimum values.
age: new FormControl(null, [
Validators.min(18)
])
Age 15 → Invalid
Age 17 → Invalid
Age 18 → Valid
Age 25 → Valid
Notice the difference:
minLength(3)
↓
Length of value
min(18)
↓
Numeric value
11. Validators.max()
Used for maximum numeric values.
age: new FormControl(null, [
Validators.max(60)
])
50 → Valid
60 → Valid
61 → Invalid
12. Validators.pattern()
Used when the value must match a specific pattern.
For example, allow only letters:
name: new FormControl('', [
Validators.pattern('^[a-zA-Z ]+$')
])
Example:
Syed Mohamed → Valid
Syed123 → Invalid
Another common example:
Validators.pattern('^[0-9]{10}$')
could be used when you specifically require exactly 10 digits.
13. Multiple Validators
We can attach multiple validators to one control:
name: new FormControl('', [
Validators.required,
Validators.minLength(3),
Validators.maxLength(50)
])
Conceptually:
Name
│
├── Required?
│
├── At least 3 characters?
│
└── Maximum 50 characters?
↓
All satisfied
↓
VALID
14. How Do You Create a Custom Validator?
Built-in validators cannot cover every business rule.
Suppose our requirement is:
The username cannot be
admin.
Angular doesn't provide:
Validators.adminNotAllowed
So we create our own validator.
Custom Validator
adminUsernameValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (control.value?.toLowerCase() === 'admin') {
return {
adminNotAllowed: true
};
}
return null;
};
}
15. Understanding the Custom Validator
A validator receives the control:
control: AbstractControl
We can access its value:
control.value
Then we check:
control.value?.toLowerCase() === 'admin'
If the value is invalid, return an error object:
return {
adminNotAllowed: true
};
If valid:
return null;
This is a critical Angular validator convention:
Validator
Invalid
↓
Return ValidationErrors object
Valid
↓
Return null
So remember:
Error object = Invalid
null= Valid
16. Attach the Custom Validator
We attach it exactly like a built-in validator:
username: new FormControl('', [
Validators.required,
this.adminUsernameValidator()
])
Now:
username = "admin"
↓
Custom Validator
↓
{ adminNotAllowed: true }
↓
INVALID
But:
username = "syed"
↓
Custom Validator
↓
null
↓
VALID
17. Display Custom Validation Error
HTML:
@if (
userForm.controls.username.hasError('adminNotAllowed')
) {
<p>Username "admin" is not allowed.</p>
}
The string:
adminNotAllowed
comes from our custom validator:
return {
adminNotAllowed: true
};
They must match.
18. Better Real-World Structure for Custom Validators
In a real project, reusable validators are commonly placed in a separate file.
For example:
src/app/
│
├── validators/
│ └── username.validator.ts
│
├── app.ts
└── app.html
username.validator.ts
import {
AbstractControl,
ValidationErrors,
ValidatorFn
} from '@angular/forms';
export function adminUsernameValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (value?.toLowerCase() === 'admin') {
return {
adminNotAllowed: true
};
}
return null;
};
}
Then import it:
import { adminUsernameValidator }
from './validators/username.validator';
And use it:
username: new FormControl('', [
Validators.required,
adminUsernameValidator()
])
This makes the validator reusable across multiple components.
19. valid vs invalid
Angular provides:
control.valid
and:
control.invalid
Example:
Name = ""
required fails
valid = false
invalid = true
After entering:
Syed
valid = true
invalid = false
20. Why Check touched?
You will frequently see:
control.invalid && control.touched
Why?
Suppose the form loads with an empty required Name field.
Technically:
Name = ""
Required validation fails
↓
INVALID
But showing an error immediately can be poor UX.
Instead:
User enters field
↓
User leaves field
↓
touched = true
↓
Show validation error
Hence:
@if (
userForm.controls.name.invalid &&
userForm.controls.name.touched
) {
<p>Please enter a valid name.</p>
}
21. Why markAllAsTouched()?
Our submit method contains:
this.userForm.markAllAsTouched();
Suppose the user doesn't touch any fields and immediately clicks Submit.
Without this, some validation messages may not appear because:
touched = false
Calling:
this.userForm.markAllAsTouched();
marks the controls as touched so validation messages can be displayed.
22. Validation Flow
The complete flow is:
User enters value
↓
FormControl value changes
↓
Angular executes validators
↓
┌───────────────┐
│ │
Valid Invalid
│ │
↓ ↓
null Error object
│ │
└───────┬───────┘
↓
Control status updated
↓
valid / invalid
↓
UI displays appropriate message
↓
Form submitted when acceptable
23. Built-in vs Custom Validator
| Built-in Validator | Custom Validator |
|---|---|
| Provided by Angular | Written by developer |
| Common validation | Business-specific validation |
Validators.required |
adminUsernameValidator() |
Validators.email |
companyEmailValidator() |
Validators.min() |
minimumWorkingAgeValidator() |
Validators.pattern() |
usernameAvailableValidator() |
Use built-in validators whenever they satisfy the requirement.
Create custom validators when your application has business-specific validation rules.
24. Important: Client Validation vs Backend Validation
If Angular validates:
Age >= 18
you should not assume the data is safe.
A client can bypass Angular and call your ASP.NET Core API directly:
Postman / curl / another application
↓
ASP.NET Core API
Therefore:
Angular Validation
↓
Good user experience
ASP.NET Core Validation
↓
Authoritative server-side validation
For an Angular + ASP.NET Core application, important validation rules should also be enforced by the backend.
25. Key Points
- Angular Reactive Forms use
Validatorsfor built-in validation. - Validators are attached to
FormControls. - Multiple validators can be applied to one control.
- Important built-in validators include
required,email,minLength,maxLength,min,max, andpattern. control.validtells whether validation succeeds.control.invalidtells whether validation fails.control.errorscontains validation errors.hasError()checks for a particular validation error.- Custom validators implement application-specific validation rules.
- A synchronous validator returns:
ValidationErrors → Invalid
null → Valid
markAllAsTouched()is useful on submit to reveal validation messages.- Angular validation is client-side; important rules must also be validated by the backend.
26. Interview Questions and Answers
1. How do you perform validation in Reactive Forms?
Attach validator functions to FormControls.
name: new FormControl('', [
Validators.required,
Validators.minLength(3)
])
Angular automatically updates the control's validity and error state.
2. What are built-in validators?
Built-in validators are commonly used validation functions provided by Angular through the Validators class.
Examples:
Validators.required
Validators.email
Validators.minLength(3)
Validators.maxLength(50)
Validators.min(18)
Validators.max(60)
Validators.pattern(...)
3. What is a custom validator?
A custom validator is a developer-defined validation function used for application-specific rules that aren't covered by Angular's built-in validators.
4. What does a validator return?
A synchronous validator returns:
ValidationErrors | null
Error object → Invalid
null → Valid
5. What is ValidationErrors?
ValidationErrors represents validation failures as key-value pairs.
Example:
{
adminNotAllowed: true
}
6. What is AbstractControl?
AbstractControl is the base class behind Angular's primary form control types, including:
FormControl
FormGroup
FormArray
Validator functions commonly accept an AbstractControl.
7. How do you check a specific validation error?
Use:
control.hasError('required')
Example:
userForm.controls.name.hasError('required')
8. Can one field have multiple validators?
Yes.
name: new FormControl('', [
Validators.required,
Validators.minLength(3),
Validators.maxLength(50)
])
9. Why use touched when displaying errors?
It prevents validation messages from appearing before the user has interacted with the field.
control.invalid && control.touched
10. Is Angular form validation enough for security?
No. Angular validation runs on the client and can be bypassed. The backend API must independently validate important input and business rules.
Interview Summary
Angular Reactive Forms perform validation by attaching validator functions to form controls. Angular provides built-in validators such as
required,minLength,maxLength,min,max, andpattern. When built-in validators don't satisfy a business rule, we create a custom validator that returns aValidationErrorsobject when invalid andnullwhen valid.