DEV Community

Cover image for Reactive vs. Template-Driven Approaches: Exploring Angular Forms
bytebantz
bytebantz

Posted on

Reactive vs. Template-Driven Approaches: Exploring Angular Forms

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('');
}
Enter fullscreen mode Exit fullscreen mode

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 = '';
}
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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) {}
Enter fullscreen mode Exit fullscreen mode

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: [''],
  }),
});
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
dminhhoang26 profile image
Henry Duong

angular forms is very hard to use for me