Comparing Template-Driven Forms and Reactive Forms in Angular
Fernando Nunes
Software Engineer | Full Stack Developer | Angular | Nodejs | Nestjs | React | AWS | Azure
Introduction
Angular provides two different approaches for handling forms: Template-Driven Forms and Reactive Forms. This tutorial will compare these two approaches, explain why Reactive Forms are often preferred, and demonstrate how to use native validators in Reactive Forms compared to Template-Driven Forms. Additionally, we'll provide practical examples of when to use each approach, such as in a CMS (Content Management System).
Template-Driven Forms
What are Template-Driven Forms?
Template-Driven Forms rely on Angular's directives to create forms directly in the template. They are straightforward and suitable for simple forms.
Example of Template-Driven Forms
Here's an example of a Template-Driven Form:
<form #form="ngForm">
<label for="name">Name:</label>
<input type="text" id="name" name="name" ngModel required>
<div *ngIf="form.controls['name']?.invalid && form.controls['name']?.touched">
Name is required.
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
Key Features of Template-Driven Forms
When to Use Template-Driven Forms
Template-Driven Forms are suitable for:
Reactive Forms
What are Reactive Forms?
Reactive Forms, also known as Model-Driven Forms, provide more robust and scalable form handling. They are defined programmatically and offer better control over the form's state and validation.
Example of Reactive Forms
Here's an example of a Reactive Form:
// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
name: ['', [Validators.required]]
});
}
onSubmit() {
if (this.form.valid) {
console.log(this.form.value);
}
}
}
<!-- app.component.html -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="name">Name:</label>
<input id="name" formControlName="name">
<div *ngIf="form.controls['name'].invalid && form.controls['name'].touched">
Name is required.
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
Key Features of Reactive Forms
Comparing Validators in Template-Driven and Reactive Forms
Template-Driven Validators
In Template-Driven Forms, validators are applied directly in the template using Angular directives. Validators such as required, minlength, maxlength, and pattern are commonly used.
<input type="text" id="name" name="name" ngModel required minlength="3">
<div *ngIf="form.controls['name']?.invalid && form.controls['name']?.touched">
<div *ngIf="form.controls['name']?.errors?.['required']">Name is required.</div>
<div *ngIf="form.controls['name']?.errors?.['minlength']">Name must be at least 3 characters long.</div>
</div>
Reactive Form Validators
In Reactive Forms, validators are applied programmatically in the component class using functions provided by Angular.
领英推荐
this.form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]]
});
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="name">Name:</label>
<input id="name" formControlName="name">
<div *ngIf="form.controls['name'].invalid && form.controls['name'].touched">
<div *ngIf="form.controls['name'].errors?.['required']">Name is required.</div>
<div *ngIf="form.controls['name'].errors?.['minlength']">Name must be at least 3 characters long.</div>
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
Advantages of Reactive Forms
1. Scalability: Easier to manage complex forms.
2. Testability: Forms and their validation logic are easier to unit test.
3. Dynamic Forms: More straightforward to add/remove form controls dynamically.
4. Control: Provides fine-grained control over the form's state and validation.
Example of Using Native Validators
Template-Driven Forms
<form #form="ngForm">
<label for="email">Email:</label>
<input type="email" id="email" name="email" ngModel required email>
<div *ngIf="form.controls['email']?.invalid && form.controls['email']?.touched">
<div *ngIf="form.controls['email']?.errors?.['required']">Email is required.</div>
<div *ngIf="form.controls['email']?.errors?.['email']">Invalid email format.</div>
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
In this example:
Reactive Forms
Validators are defined programmatically using Angular's Validators class.
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]]
});
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="email">Email:</label>
<input id="email" formControlName="email">
<div *ngIf="form.controls['email'].invalid && form.controls['email'].touched">
<div *ngIf="form.controls['email'].errors?.['required']">Email is required.</div>
<div *ngIf="form.controls['email'].errors?.['email']">Invalid email format.</div>
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
In this example:
Why Choose Reactive Forms for a CMS?
In a Content Management System (CMS), forms are often complex and require dynamic validation rules based on user roles, content types, and other criteria. Reactive Forms are particularly suited for such scenarios due to:
// cms-form.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-cms-form',
templateUrl: './cms-form.component.html',
})
export class CmsFormComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
title: ['', [Validators.required]],
content: ['', [Validators.required, Validators.minLength(10)]],
author: ['', [Validators.required]],
publishDate: ['', [Validators.required]]
});
}
setAdvancedValidation() {
if (this.form.controls['author'].value === 'admin') {
this.form.controls['content'].setValidators([Validators.required, Validators.minLength(50)]);
} else {
this.form.controls['content'].setValidators([Validators.required, Validators.minLength(10)]);
}
this.form.controls['content'].updateValueAndValidity();
}
onSubmit() {
if (this.form.valid) {
console.log(this.form.value);
}
}
}
<!-- cms-form.component.html -->
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<label for="title">Title:</label>
<input id="title" formControlName="title">
<div *ngIf="form.controls['title'].invalid && form.controls['title'].touched">
<div *ngIf="form.controls['title'].errors?.['required']">Title is required.</div>
</div>
<label for="content">Content:</label>
<textarea id="content" formControlName="content"></textarea>
<div *ngIf="form.controls['content'].invalid && form.controls['content'].touched">
<div *ngIf="form.controls['content'].errors?.['required']">Content is required.</div>
<div *ngIf="form.controls['content'].errors?.['minlength']">Content must be at least {{ form.controls['content'].errors?.['minlength'].requiredLength }} characters long.</div>
</div>
<label for="author">Author:</label>
<input id="author" formControlName="author" (blur)="setAdvancedValidation()">
<div *ngIf="form.controls['author'].invalid && form.controls['author'].touched">
<div *ngIf="form.controls['author'].errors?.['required']">Author is required.</div>
</div>
<label for="publishDate">Publish Date:</label>
<input type="date" id="publishDate" formControlName="publishDate">
<div *ngIf="form.controls['publishDate'].invalid && form.controls['publishDate'].touched">
<div *ngIf="form.controls['publishDate'].errors?.['required']">Publish Date is required.</div>
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
Conclusion
Both Template-Driven and Reactive Forms have their use cases in Angular. Template-Driven Forms are suitable for simple forms and quick prototypes, while Reactive Forms offer better scalability, control, and testability for more complex applications. Reactive Forms are particularly advantageous in scenarios like a CMS where dynamic validation and complex form management are required. Understanding the differences and advantages can help you choose the right approach for your project's needs.
Software Engineer | Full Stack Developer | Ruby On Rails | React | AWS
2 个月Thanks for sharing Fernando Nunes!
FullStack Developer | Java | Spring | Angular @ FATTO Consultoria e Sistemas
2 个月Awesome content to read.
Fullstack Software Engineer | PHP | Laravel | ReactJs | AWS | Docker
2 个月Great article Fernando Nunes!
Full Stack Engineer | React | Node | JavaScript | Typescript | Next | MERN Developer
2 个月That's a great content Fernando Nunes ! One stuff that I miss from working with Angular are those form control, they are reeealy helpful !