← Back to Articles         
Angular Form Validation

Angular Form Validation

Published on 01 Oct 2026     10 min read Angular
Angular forms

Angular Form Validation

Let's cover these together:

  1. How do you perform form validation?
  2. What are built-in validators?
  3. 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 Validators for 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, and pattern.
  • control.valid tells whether validation succeeds.
  • control.invalid tells whether validation fails.
  • control.errors contains 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, email, minLength, maxLength, min, max, and pattern. When built-in validators don't satisfy a business rule, we create a custom validator that returns a ValidationErrors object when invalid and null when valid.

← Back to Articles