Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Angular Form Validation

Form Validation Overview

Angular provides a powerful validation system for both Reactive and Template-driven forms. Validators can be built-in (required, minLength, email) or custom. Angular tracks the validity state of each control and the form as a whole, making it easy to show error messages and disable submit buttons.

Built-in ValidatorDescriptionUsage
Validators.requiredField must not be emptyrequired attribute or Validators.required
Validators.minLength(n)Minimum character countminlength="3" or Validators.minLength(3)
Validators.maxLength(n)Maximum character countmaxlength="50" or Validators.maxLength(50)
Validators.emailValid email formatemail attribute or Validators.email
Validators.pattern(regex)Must match regex patternValidators.pattern(/^[0-9]+$/)
Validators.min(n)Minimum numeric valueValidators.min(0)
Validators.max(n)Maximum numeric valueValidators.max(100)

Reactive Form Validation

In reactive forms, validators are added programmatically in the component class. This gives full control over validation logic and makes it easy to test.

Reactive Form with Validation
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';

@Component({
    selector: 'app-register',
    standalone: true,
    imports: [ReactiveFormsModule],
    templateUrl: './register.component.html'
})
export class RegisterComponent {
    private fb = inject(FormBuilder);

    form = this.fb.group({
        name: ['', [Validators.required, Validators.minLength(2)]],
        email: ['', [Validators.required, Validators.email]],
        password: ['', [Validators.required, Validators.minLength(8)]],
        age: [null, [Validators.required, Validators.min(18), Validators.max(120)]]
    });

    // Convenience getters for template access
    get name()     { return this.form.get('name')!; }
    get email()    { return this.form.get('email')!; }
    get password() { return this.form.get('password')!; }
    get age()      { return this.form.get('age')!; }

    onSubmit() {
        if (this.form.valid) {
            console.log('Form submitted:', this.form.value);
        } else {
            this.form.markAllAsTouched();
        }
    }
}
<form [formGroup]="form" (ngSubmit)="onSubmit()">

    <div>
        <label>Name</label>
        <input formControlName="name" />
        @if (name.invalid && name.touched) {
            @if (name.errors?.['required']) {
                <span class="text-danger">Name is required.</span>
            }
            @if (name.errors?.['minlength']) {
                <span class="text-danger">Name must be at least 2 characters.</span>
            }
        }
    </div>

    <div>
        <label>Email</label>
        <input formControlName="email" type="email" />
        @if (email.invalid && email.touched) {
            @if (email.errors?.['required']) {
                <span class="text-danger">Email is required.</span>
            }
            @if (email.errors?.['email']) {
                <span class="text-danger">Enter a valid email address.</span>
            }
        }
    </div>

    <button type="submit" [disabled]="form.invalid">Register</button>

</form>

Custom Validators

When built-in validators are not enough, you can create custom validators. A validator is a function that takes an AbstractControl and returns either null (valid) or a ValidationErrors object (invalid).

Custom Validators
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

// 1. No whitespace validator
export function noWhitespaceValidator(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
        const hasWhitespace = (control.value || '').includes(' ');
        return hasWhitespace ? { whitespace: true } : null;
    };
}

// 2. Password strength validator
export function passwordStrengthValidator(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
        const value = control.value || '';
        const hasUpper   = /[A-Z]/.test(value);
        const hasLower   = /[a-z]/.test(value);
        const hasNumber  = /[0-9]/.test(value);
        const hasSpecial = /[!@#$%^&*]/.test(value);
        const isStrong   = hasUpper && hasLower && hasNumber && hasSpecial;
        return isStrong ? null : { weakPassword: { hasUpper, hasLower, hasNumber, hasSpecial } };
    };
}

// 3. Cross-field validator — passwords must match
export function passwordMatchValidator(control: AbstractControl): ValidationErrors | null {
    const password        = control.get('password');
    const confirmPassword = control.get('confirmPassword');
    if (!password || !confirmPassword) return null;
    return password.value === confirmPassword.value ? null : { passwordMismatch: true };
}
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { noWhitespaceValidator, passwordStrengthValidator, passwordMatchValidator } from './custom-validators';

@Component({
    selector: 'app-signup',
    standalone: true,
    imports: [ReactiveFormsModule],
    template: `
        <form [formGroup]="form" (ngSubmit)="onSubmit()">
            <input formControlName="username" placeholder="Username" />
            @if (form.get('username')?.errors?.['whitespace']) {
                <span class="text-danger">No spaces allowed.</span>
            }

            <input formControlName="password" type="password" placeholder="Password" />
            @if (form.get('password')?.errors?.['weakPassword']) {
                <span class="text-danger">Password needs uppercase, lowercase, number and special char.</span>
            }

            <input formControlName="confirmPassword" type="password" placeholder="Confirm Password" />
            @if (form.errors?.['passwordMismatch']) {
                <span class="text-danger">Passwords do not match.</span>
            }

            <button type="submit" [disabled]="form.invalid">Sign Up</button>
        </form>
    `
})
export class SignupComponent {
    private fb = inject(FormBuilder);

    form = this.fb.group({
        username: ['', [Validators.required, noWhitespaceValidator()]],
        password: ['', [Validators.required, passwordStrengthValidator()]],
        confirmPassword: ['', Validators.required]
    }, { validators: passwordMatchValidator });

    onSubmit() {
        if (this.form.valid) console.log(this.form.value);
    }
}

Form Control States

Angular tracks the state of each form control. Understanding these states helps you show the right error messages at the right time.

StatePropertyMeaning
Pristinecontrol.pristineUser has not changed the value yet
Dirtycontrol.dirtyUser has changed the value
Untouchedcontrol.untouchedUser has not focused and left the field
Touchedcontrol.touchedUser has focused and left the field
Validcontrol.validAll validators pass
Invalidcontrol.invalidAt least one validator fails
Pendingcontrol.pendingAsync validator is running

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.