Advanced Forms

Advanced Angular Forms

Enterprise applications require highly dynamic and heavily validated forms.

We previously explored the basics of Reactive Forms using FormGroup and FormControl.

Now, we will explore advanced concepts like FormArray and Custom Validators.


Dynamic Forms with FormArray

Sometimes, you do not know how many input fields a user will need.

For example, allowing a user to add multiple different phone numbers or email addresses dynamically.

Angular provides FormArray to handle arrays of form controls that can grow or shrink at runtime.

FormArray Example:

import { FormArray, FormControl, FormGroup } from '@angular/forms';

export class ProfileComponent { profileForm = new FormGroup({ aliases: new FormArray([ new FormControl('Hero') ]) });

get aliases() { return this.profileForm.get('aliases') as FormArray; }

addAlias() { // Dynamically adds a new input field to the form! this.aliases.push(new FormControl('')); } }


Custom Validators

Angular provides built-in validators like Validators.required and Validators.email.

However, business logic often requires custom validation rules, like checking if a username is already taken.

You can create custom validators by writing a simple function that returns an error object if the validation fails.

Custom Validator Example:

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

// A custom validator preventing the word "admin" export function forbiddenNameValidator(control: AbstractControl): ValidationErrors | null { const forbidden = /admin/i.test(control.value);

// Returns an error object if invalid, or null if valid return forbidden ? { forbiddenName: { value: control.value } } : null; }

// Applying it to a FormControl const username = new FormControl('', [forbiddenNameValidator]);


Cross-Field Validation

Sometimes validation depends on multiple fields simultaneously.

A classic example is confirming that a "Password" field and a "Confirm Password" field match exactly.

To achieve this, you attach a custom validator to the parent FormGroup instead of individual FormControls.

Cross-Field Validation:

export const passwordMatchValidator: ValidatorFn = (control: AbstractControl) => {
  const password = control.get('password');
  const confirm = control.get('confirmPassword');

return password && confirm && password.value !== confirm.value ? { mismatch: true } : null; };


Exercise 1 of 2

?

Which Angular class is specifically designed to manage a dynamic, resizable list of form controls?

Exercise 2 of 2

?

What must a custom validator function return if the form control's value is perfectly valid?