Angular Reactive Forms: A Comprehensive Guide to Building Dynamic Forms

Reactive forms in Angular are a technique for building forms that provide a reactive programming approach to form handling. Reactive forms are built using a set of classes and services that allow developers to create dynamic and interactive forms with complex validation and logic.

Here is a detailed explanation of some of the key components of reactive forms in Angular:

Form Builder

The form builder is a service provided by Angular that is used to create and configure reactive forms. It allows developers to create instances of FormGroup, FormArray, and FormControl classes, which are the building blocks of reactive forms.

Here is an example code of how to create a form using form builder:

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

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.css']
})
export class ContactComponent {
  contactForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.contactForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', Validators.required],
      message: ''
    });
  }
}

In the code above, the FormBuilder service is injected into the component's constructor. The createForm method is then used to create an instance of the FormGroup class and configure it with a set of form controls.

Form Group

A Form Group is a collection of Form Controls that are grouped together. It is used to represent a hierarchical structure of form data. A Form Group can contain other Form Groups and Form Controls. A Form Group is defined using the FormBuilder service.

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

@Component({
  selector: 'app-my-form',
  template: `
    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
      <label>Name:</label>
      <input type="text" formControlName="name" required>

      <label>Email:</label>
      <input type="email" formControlName="email" required>

      <button type="submit">Submit</button>
    </form>
  `
})
export class MyFormComponent {
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.myForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]]
    });
  }

  onSubmit() {
    console.log(this.myForm.value);
  }
}

Form Controls

A form control is an instance of the FormControl class, which is used to manage the value and validation state of an individual form control element, such as an input field.

Here is an example code of how to create a form control using form builder:

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

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.css']
})
export class ContactComponent {
  contactForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.contactForm = this.fb.group({
      name: ['', Validators.required],
      email: ['', Validators.required],
      message: ''
    });
  }

  get name() { return this.contactForm.get('name'); }
  get email() { return this.contactForm.get('email'); }
  get message() { return this.contactForm.get('message'); }
}

In the code above, the name, email, and message properties are getters that return instances of the FormControl class. These form controls are then used in the template to bind to input fields.

Form Arrays

Form Arrays are a type of form control that allows you to manage a dynamic list of child form controls. With Form Arrays, you can add and remove form controls from the array as needed, and easily iterate over the array to generate input fields in the form.

Here's an example code snippet that demonstrates how to use the Form Builder and Form Arrays to create a reactive form in Angular:

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

@Component({
  selector: 'app-my-form',
  template: `
    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
      <label>Name:</label>
      <input type="text" formControlName="name">

      <div formArrayName="hobbies">
        <div *ngFor="let hobby of hobbies.controls; let i = index">
          <input type="text" [formControlName]="i">
          <button type="button" (click)="removeHobby(i)">Remove</button>
        </div>
        <button type="button" (click)="addHobby()">Add Hobby</button>
      </div>

      <button type="submit">Submit</button>
    </form>
  `
})
export class MyFormComponent {
  myForm: FormGroup;

  constructor(private fb: FormBuilder) {
    this.createForm();
  }

  createForm() {
    this.myForm = this.fb.group({
      name: ['', Validators.required],
      hobbies: this.fb.array([
        this.fb.control('')
      ])
    });
  }

  get hobbies() {
    return this.myForm.get('hobbies') as FormArray;
  }

  addHobby() {
    this.hobbies.push(this.fb.control(''));
  }

  removeHobby(index: number) {
    this.hobbies.removeAt(index);
  }

  onSubmit() {
    console.log(this.myForm.value);
  }
}

In this example, the MyFormComponent defines a form with two fields: a name field, and a hobbies field, which is a Form Array that can have any number of hobby values.

The createForm() method defines the structure of the form using the Form Builder. The name field is a basic form control, while the hobbies field is defined as a Form Array that contains a single hobby form control.

The get hobbies() method returns the Form Array control for the hobbies field, which is used in the template to iterate over the list of hobbies and generate input fields. The addHobby() method is used to add a new hobby input field to the hobbies Form Array, while the removeHobby() method is used to remove a hobby input field from the hobbies Form Array, based on its index.

The template for the MyFormComponent displays the form and its fields, and binds to the form using the formGroup directive. The formArrayName directive is used to bind to the hobbies Form Array, while the *ngFor directive is used to iterate over the array and generate input fields for each hobby. The onSubmit() method is called when the form is submitted, and simply logs the form value to the console.

Watch sample code on GitHub