Day 6 β Angular Forms: Template Forms, Reactive Forms, Validation & Signals
π Day 6 β Angular Forms: Template Forms, Reactive Forms, Validation & Signals
Zero to Hero β Hands-on Angular Tutorial
Today you will learn:
- βοΈ Template-Driven Forms (Simple)
- βοΈ Reactive Forms (Powerful)
- βοΈ Form Validation (Required, Email, Pattern)
- βοΈ Handling Form Events
- βοΈ Error Messages
- βοΈ Integrating Signals with Forms
- βοΈ Saving Data to an API
Forms are the backbone of most applications. By the end of this, youβll master both ways to build them.
π¦ 1. Which Form Type Should You Choose?
| Feature | Template-Driven | Reactive Forms |
|---|---|---|
| Best For | Simple login, contact forms | Complex, dynamic, enterprise apps |
| Structure | Defined in HTML | Defined in TypeScript |
| Scalability | Harder to scale | Very easy to scale |
| Testing | Harder | Easier |
We will build BOTH.
π© 2. Template-Driven Forms (The Easy Way)
Designed for simple use cases.
Step 1: Import FormsModule.
app.component.ts:
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
standalone: true,
imports: [FormsModule],
templateUrl: './app.component.html'
})
export class AppComponent {
user = {
name: '',
email: ''
};
onSubmit() {
console.log('Form Submitted!', this.user);
}
}
Step 2: Build the HTML.
app.component.html:
<form (ngSubmit)="onSubmit()" #form="ngForm">
<div>
<label>Name:</label>
<input type="text" name="name" [(ngModel)]="user.name" required />
</div>
<div>
<label>Email:</label>
<input type="email" name="email" [(ngModel)]="user.email" required email />
</div>
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
- βοΈ
[(ngModel)]= Two-way binding - βοΈ
#form="ngForm"= Access form state - βοΈ
required email= HTML5-style validation
Simple. Fast. Effective.
π§ 3. Reactive Forms (The Pro Way)
Designed for complexity, validation, and control.
Step 1: Import ReactiveFormsModule.
app.component.ts:
import { Component, inject } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
import { NgIf } from '@angular/common';
@Component({
standalone: true,
imports: [ReactiveFormsModule, NgIf],
templateUrl: './app.component.html'
})
export class AppComponent {
fb = inject(FormBuilder);
loginForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]]
});
onSubmit() {
if (this.loginForm.valid) {
console.log('Login Payload:', this.loginForm.value);
}
}
}
Step 2: Build the HTML.
app.component.html:
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<div>
<label>Email:</label>
<input formControlName="email" type="email" />
<div *ngIf="loginForm.get('email')?.invalid && loginForm.get('email')?.touched">
β Invalid Email
</div>
</div>
<div>
<label>Password:</label>
<input formControlName="password" type="password" />
<div *ngIf="loginForm.get('password')?.hasError('minlength')">
β οΈ Password must be 6+ chars
</div>
</div>
<button type="submit" [disabled]="loginForm.invalid">Login</button>
</form>
- βοΈ
[formGroup]connect the form - βοΈ
formControlNamebinds inputs - βοΈ Validation logic stays in TypeScript
- βοΈ
Validatorsprovides powerful rules
π₯ 4. Advanced Validation (Signals + Custom Validators)
Letβs create a custom validator that forbids the name βAdminβ.
validators/no-admin.validator.ts:
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function noAdminvalidator(control: AbstractControl): ValidationErrors | null {
return control.value.toLowerCase() === 'admin' ? { isBlocked: true } : null;
}
Usage:
username: ['', [Validators.required, noAdminvalidator]]
Template Error:
<p *ngIf="form.get('username')?.hasError('isBlocked')">
β You cannot use 'Admin' as a username.
</p>
π« 5. Dynamic Forms (FormArray)
What if you need a dynamic list of skills?
app.component.ts:
skills = this.fb.array([]);
addSkill() {
this.skills.push(this.fb.control(''));
}
app.component.html:
<div formArrayName="skills">
<div *ngFor="let skill of skills.controls; let i = index">
<input [formControlName]="i" placeholder="Skill {{i + 1}}" />
</div>
</div>
<button (click)="addSkill()">+ Add Skill</button>
- βοΈ Great for βAdd multiple phone numbersβ or dynamic fields.
π¦ 6. Connecting Forms to API (CRUD Integration)
app.component.ts:
register() {
if (this.form.valid) {
this.http.post('https://api.example.com/register', this.form.value)
.subscribe({
next: () => alert('Success!'),
error: () => alert('Error!')
});
}
}
π End of Day 6 β What You Learned
Today you specialized in data collection:
- βοΈ Template-Driven Forms: Quick & Easy
- βοΈ Reactive Forms: Scalable & Testable
- βοΈ Validators: Built-in & Custom
- βοΈ FormArray: Dynamic Lists
- βοΈ Form Submission: Handling data
You are now capable of building:
- Login/Register pages
- Checkout flows
- Complex data entry systems
π§ͺ Day 6 Challenge
Build:
π A βJob Application Formβ
Requirements:
- Use Reactive Forms.
- Fields:
- Name (Required)
- Email (Required, Email format)
- Experience (Number, must be > 0)
- Skills (Dynamic FormArray - Add/Remove skills)
- Show error messages for invalid fields.
- Disable submit button if form is invalid.