Reactive vs. Template-Driven Approaches: Exploring Angular Forms
Tags: angular, webdev, javascript
Published: 2024-06-02 05:39:21

Handling user input with forms is a fundamental aspect of many applications, allowing users to log in, update profiles, and perform various data-entry tasks. In Angular development, managing user input through forms is a fundamental aspect. Angular offers two main approaches for handling forms: reactive forms and template-driven forms. Let’s delve into each approach to understand their characteristics, use cases, and implementation examples.

## Common Building Blocks:

Both reactive and template-driven forms use common building blocks:

**- FormControl:** Tracks the value and validation status of an 
    individual form control.
**- FormGroup:** Tracks values and status for a fixed set of form 
    controls.
**- FormArray:** Tracks values and status for an array of form 
    controls. FormArray allows you to add and remove form 
    controls at runtime.
**- ControlValueAccessor:** Creates a bridge between Angular 
  FormControl instances and built-in DOM elements. It serves as a 
  middleman, helping Angular understand how to read and update 
  the value of custom form controls.

## Reactive Forms

In reactive forms, you define the form model directly in the component class. The form model is explicitly created using **FormControl** instances, providing direct access to the form API.

Reactive forms offer scalability, reusability, and ease of testing, making them a preferred choice for complex applications.

## Key Features:

**· Scalability**
Reactive forms, with direct access to the form API and synchronous data flow, are more scalable, reusable, and easier to test than template-driven forms. If scalability is essential, opt for reactive forms.

**· Data Flow**
In reactive forms, each form element in the view is directly linked to a FormControl instance. Updates from the view to the model and vice versa are synchronous.

## View-to-Model Data Flow
1. The user interacts with an input field by typing a value, such 
   as “Blue.”
2. The input element emits an “input” event, conveying the latest 
   value.
3. The ControlValueAccessor, responsible for handling events on 
   the input element, promptly relays the new value to the 
   associated FormControl instance.
4. The FormControl instance emits the updated value through the 
   valueChanges observable.
5. Any subscribers to the valueChanges observable receive the new 
   value, ensuring real-time synchronization between the view and 
   the model.

## Model-to-View Data Flow

1. A programmatic change to the model, initiated by calling the 
   **setValue()** method on the FormControl instance, updates its 
   value.
2. The updated value is emitted through the valueChanges 
   observable.
3. Subscribers to the valueChanges observable are notified of the 
   change, allowing them to react accordingly.
4. The control value accessor associated with the input element 
   updates the view, ensuring that the displayed value reflects 
   the updated model state.

**· Validation**
_Form validation_ is used to ensure that user input is complete and correct.

Define custom validators as functions receiving a control to validate.

**· Mutability**
Reactive forms keep the data model pure by providing it as an immutable structure. Each change triggers the creation of a new data model rather than updating the existing data model, improving change detection efficiency e.g. each update to the favorite color triggers the **FormControl** instance to return a new value, maintaining the immutability of the data model.

**Reactive Form Example:**

```
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-reactive-favorite-color',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    Favorite Color: <input type="text" [formControl]="favoriteColorControl">
  `,
})
export class ReactiveFavoriteColorComponent {
  favoriteColorControl = new FormControl('');
}
```
In this example, the **favoriteColorControl** represents the **FormControl** instance, which acts as the form model. The **formControlName / [formControl]** directive binds this **FormControl** to the **\<input\>** element, enabling seamless interaction between the view and the component class.

## Template-Driven Forms

**Template-driven** forms have an **implicit** form model created by directives in the template. The **NgModel** directive manages a **FormControl** instance for a given form element. They are suitable for simple scenarios and are easy to add to an app.

## Key Features:

**· Data Flow**
In template-driven forms, form elements are linked to a directive managing the form model internally. Data flow is asynchronous.

**View-to-Model Data Flow**

1. User input, such as typing “Blue” into an input element, triggers an “input” event.
2. The input element emits the event containing the entered 
   value.
3. The control value accessor, attached to the input, invokes the 
   **setValue()** method on the underlying FormControl instance.
4. Similar to reactive forms, the FormControl emits the updated 
   value through the valueChanges observable.
5. Additionally, the control value accessor triggers the 
   **NgModel.viewToModelUpdate()** method, emitting an 
   **ngModelChange** event.
6. Utilizing **two-way data binding**, the component’s property 
   bound to **NgModel** is updated with the emitted value, 
   ensuring synchronization between the view and the component.

**Model-to-View Data Flow**

1. A change in the model, such as the favoriteColor property 
  transitioning from “Blue” to “Red,” triggers change detection.
2. During change detection, the **ngOnChanges** lifecycle hook is 
   invoked on the **NgModel** directive instance.
3. The **ngOnChanges()** method queues an asynchronous task to 
   update the **FormControl** instance’s value.
4. Upon completion of change detection, the task to set the 
   FormControl value is executed.
5. The updated value is emitted through the **valueChanges** 
   observable, notifying subscribers of the change.
6. The control value accessor updates the view element with the 
   latest model value, ensuring consistency between the model and 
   the view.

**· Validation**
Validation is tied to template directives

**· Mutability**
Template-driven forms rely on mutability with two-way data binding, making change detection less efficient as there are no unique changes to track on the data model e.g. the favorite color property is directly modified to its new value, reflecting the mutable nature of the data model

**Template-Driven Form Example:**

```
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-template-favorite-color',
  standalone: true,
  imports: [FormsModule],
  template: `
    Favorite Color: <input type="text" [(ngModel)]="favoriteColor">
  `,
})
export class FavoriteColorComponent {
  favoriteColor = '';
}
```
In this example, the **[(ngModel)]** directive binds the **favoriteColor** property to the input element, effectively creating and managing a FormControl instance behind the scenes. When you use **[(ngModel)]** on an element, you must define a name attribute for that element. Angular uses the assigned name to register the element with the **NgForm** directive attached to the parent **\<form\>** element.

## Using the FormBuilder service to generate controls

**Step 1: Import the FormBuilder Class**

The first step is to import the FormBuilder class from the **@angular/forms** package into your Angular component. This class provides the necessary methods for creating form controls, form groups, and form arrays.

```
import { FormBuilder } from '@angular/forms';
```
**Step 2: Inject the FormBuilder Service**

Inject the FormBuilder service into your component by adding it to the constructor. This allows you to access the FormBuilder methods within your component.

```
constructor(private formBuilder: FormBuilder) {}
```
**Step 3: Generate Form Controls**

Now, let’s create the form controls using the **FormBuilder** service. We’ll use the **group()** method to create a form group, which represents a collection of form controls.

```
profileForm = this.formBuilder.group({
  firstName: [''],
  lastName: [''],
  address: this.formBuilder.group({
    street: [''],
    city: [''],
    state: [''],
    zip: [''],
  }),
});
```
In conclusion, Angular offers developers powerful tools for managing user input through reactive and template-driven forms. Understanding the strengths and use cases of each approach enables developers to build robust and efficient web applications.