Form validation is a crucial aspect of any web application to ensure that the data entered by users is correct and meets the required criteria. Angular provides robust support for form validation, both for template-driven and reactive forms. In this section, we will cover the following topics:

  1. Introduction to Form Validation
  2. Built-in Validators
  3. Custom Validators
  4. Displaying Validation Messages
  5. Practical Exercises

  1. Introduction to Form Validation

Form validation in Angular can be achieved using two approaches:

  • Template-driven forms: Validation is defined in the template using directives.
  • Reactive forms: Validation is defined in the component class using form control objects.

  1. Built-in Validators

Angular provides several built-in validators that can be used to validate form controls. These include:

  • Required: Ensures the field is not empty.
  • MinLength: Ensures the field has a minimum length.
  • MaxLength: Ensures the field does not exceed a maximum length.
  • Pattern: Ensures the field matches a specific regex pattern.
  • Email: Ensures the field contains a valid email address.

Example: Template-driven Form with Built-in Validators

<form #userForm="ngForm">
  <div>
    <label for="name">Name:</label>
    <input type="text" id="name" name="name" ngModel required minlength="3">
    <div *ngIf="userForm.controls.name?.invalid && userForm.controls.name?.touched">
      <small *ngIf="userForm.controls.name?.errors?.required">Name is required.</small>
      <small *ngIf="userForm.controls.name?.errors?.minlength">Name must be at least 3 characters long.</small>
    </div>
  </div>
  <button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>

Example: Reactive Form with Built-in Validators

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-user-form',
  template: `
    <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
      <div>
        <label for="name">Name:</label>
        <input type="text" id="name" formControlName="name">
        <div *ngIf="userForm.controls.name.invalid && userForm.controls.name.touched">
          <small *ngIf="userForm.controls.name.errors.required">Name is required.</small>
          <small *ngIf="userForm.controls.name.errors.minlength">Name must be at least 3 characters long.</small>
        </div>
      </div>
      <button type="submit" [disabled]="userForm.invalid">Submit</button>
    </form>
  `
})
export class UserFormComponent {
  userForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.userForm = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]]
    });
  }

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

  1. Custom Validators

In addition to built-in validators, Angular allows you to create custom validators to meet specific validation requirements.

Example: Custom Validator for Username

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

export function usernameValidator(control: AbstractControl): ValidationErrors | null {
  const forbidden = /admin/.test(control.value);
  return forbidden ? { forbiddenName: { value: control.value } } : null;
}

Using the Custom Validator in a Reactive Form

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { usernameValidator } from './username-validator';

@Component({
  selector: 'app-user-form',
  template: `
    <form [formGroup]="userForm" (ngSubmit)="onSubmit()">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" formControlName="username">
        <div *ngIf="userForm.controls.username.invalid && userForm.controls.username.touched">
          <small *ngIf="userForm.controls.username.errors.forbiddenName">Username cannot contain 'admin'.</small>
        </div>
      </div>
      <button type="submit" [disabled]="userForm.invalid">Submit</button>
    </form>
  `
})
export class UserFormComponent {
  userForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.userForm = this.fb.group({
      username: ['', [Validators.required, usernameValidator]]
    });
  }

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

  1. Displaying Validation Messages

Displaying validation messages helps users understand what they need to correct in the form. This can be done using Angular's conditional rendering with *ngIf.

Example: Displaying Validation Messages

<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" formControlName="email">
    <div *ngIf="userForm.controls.email.invalid && userForm.controls.email.touched">
      <small *ngIf="userForm.controls.email.errors.required">Email is required.</small>
      <small *ngIf="userForm.controls.email.errors.email">Invalid email address.</small>
    </div>
  </div>
  <button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>

  1. Practical Exercises

Exercise 1: Create a Registration Form

Create a registration form with the following fields and validations:

  • Username: Required, minimum length 5, custom validator to disallow 'admin'.
  • Email: Required, valid email format.
  • Password: Required, minimum length 8.
  • Confirm Password: Required, must match the password.

Solution

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { usernameValidator } from './username-validator';

@Component({
  selector: 'app-registration-form',
  template: `
    <form [formGroup]="registrationForm" (ngSubmit)="onSubmit()">
      <div>
        <label for="username">Username:</label>
        <input type="text" id="username" formControlName="username">
        <div *ngIf="registrationForm.controls.username.invalid && registrationForm.controls.username.touched">
          <small *ngIf="registrationForm.controls.username.errors.required">Username is required.</small>
          <small *ngIf="registrationForm.controls.username.errors.minlength">Username must be at least 5 characters long.</small>
          <small *ngIf="registrationForm.controls.username.errors.forbiddenName">Username cannot contain 'admin'.</small>
        </div>
      </div>
      <div>
        <label for="email">Email:</label>
        <input type="email" id="email" formControlName="email">
        <div *ngIf="registrationForm.controls.email.invalid && registrationForm.controls.email.touched">
          <small *ngIf="registrationForm.controls.email.errors.required">Email is required.</small>
          <small *ngIf="registrationForm.controls.email.errors.email">Invalid email address.</small>
        </div>
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" id="password" formControlName="password">
        <div *ngIf="registrationForm.controls.password.invalid && registrationForm.controls.password.touched">
          <small *ngIf="registrationForm.controls.password.errors.required">Password is required.</small>
          <small *ngIf="registrationForm.controls.password.errors.minlength">Password must be at least 8 characters long.</small>
        </div>
      </div>
      <div>
        <label for="confirmPassword">Confirm Password:</label>
        <input type="password" id="confirmPassword" formControlName="confirmPassword">
        <div *ngIf="registrationForm.controls.confirmPassword.invalid && registrationForm.controls.confirmPassword.touched">
          <small *ngIf="registrationForm.controls.confirmPassword.errors.required">Confirm Password is required.</small>
          <small *ngIf="registrationForm.controls.confirmPassword.errors.mustMatch">Passwords must match.</small>
        </div>
      </div>
      <button type="submit" [disabled]="registrationForm.invalid">Register</button>
    </form>
  `
})
export class RegistrationFormComponent {
  registrationForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.registrationForm = this.fb.group({
      username: ['', [Validators.required, Validators.minLength(5), usernameValidator]],
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8)]],
      confirmPassword: ['', Validators.required]
    }, { validator: this.mustMatch('password', 'confirmPassword') });
  }

  mustMatch(password: string, confirmPassword: string) {
    return (formGroup: FormGroup) => {
      const control = formGroup.controls[password];
      const matchingControl = formGroup.controls[confirmPassword];

      if (matchingControl.errors && !matchingControl.errors.mustMatch) {
        return;
      }

      if (control.value !== matchingControl.value) {
        matchingControl.setErrors({ mustMatch: true });
      } else {
        matchingControl.setErrors(null);
      }
    };
  }

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

Conclusion

In this section, we covered the essentials of form validation in Angular, including built-in validators, custom validators, and displaying validation messages. We also provided a practical exercise to create a registration form with various validation rules. Understanding and implementing form validation is crucial for ensuring data integrity and providing a better user experience. In the next section, we will delve into dynamic forms and how to handle complex form scenarios in Angular.

© Copyright 2024. All rights reserved