Logo

Angular Forms

Validators

Garbage in, Garbage out. Validators are the gatekeepers of your app. They ensure that only clean, correct data reaches your backend.

There is a golden rule in development: Never trust user input. Whether intentional or accidental – incorrect data format can crash your backend or corrupt your database.

Angular makes this easy. Instead of writing manual `if (email.includes('@'))` checks, I simply give the framework a list of rules (`Validators`). Angular takes care of the checking, state management, and CSS classes automatically.

1. The Playground

Type into the fields below and observe the errors object on the right. Watch how the status changes from INVALID to VALID only when all rules are met.

Form State "X-Ray View"
Form Status: INVALID
Username Errors:
{
  "required": true
}
Email Errors:
{
  "required": true
}
Password Mismatch:
 null 

2. Implementation

The implementation consists of two steps: Defining the rules in TypeScript (The Brain) and displaying feedback in HTML (The View).

Step 1: Define the Rules

I always define validators directly when initializing the FormGroup. Multiple validators are passed as an array.
Tip: Use Validators.pattern() for powerful Regex checks (e.g., for passwords).

register.component.ts
import { Component, inject } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';

export class RegisterComponent {
  private fb = inject(FormBuilder);

  registerForm = this.fb.group({
    // Syntax: ['Initial Value', [Sync Validators], [Async Validators]]

    username: ['', [
      Validators.required,
      Validators.minLength(3)
    ]],

    email: ['', [
      Validators.required,
      Validators.email // Checks for @ and domain structure
    ]],

    password: ['', [
      Validators.required,
      // Regex: At least 8 chars, one uppercase letter
      Validators.pattern(/^(?=.*[A-Z]).{8,}$/)
    ]]
  });

  // Pro Tip: Getters make the HTML cleaner
  get email() { return this.registerForm.get('email'); }
}

Step 2: Provide Feedback

An error that the user can't see doesn't exist for them. I usually check for touched (field was visited) OR dirty (user typed something). This prevents aggressive red warnings from appearing before the user has even started typing.

register.component.html
<div class="form-group">
  <label>Email Address</label>
  <input type="email" formControlName="email">

  <!--
    Feedback Logic:
    1. 'touched': User clicked inside and left (Blur)
    2. 'dirty': User actually typed something
    3. 'invalid': The rules were violated
  -->
  @if (email?.invalid && (email?.touched || email?.dirty)) {

    <div class="error-container">
      @if (email?.hasError('required')) {
        <span>Email is required.</span>
      }
      @if (email?.hasError('email')) {
        <span>Please enter a valid email address.</span>
      }
    </div>

  }
</div>
💡

Async Validators: Sometimes a synchronous check isn't enough (e.g., "Is this username already taken?").
For this, Angular has Asynchronous Validators. They return an Observable, query the backend, and are passed as the third argument in the configuration.