Welcome to our comprehensive guide on Angular Forms! Forms are the backbone of most web applications, and Angular provides two powerful approaches to handle them. Let’s explore both in depth.
Two Form Paradigms in Angular
Feature | Template-Driven | Reactive (Model-Driven) |
---|---|---|
Setup | FormsModule | ReactiveFormsModule |
Data Model | Implicit (two-way binding) | Explicit (FormControl objects) |
Validation | Directive-based | Function-based |
Testability | Harder to test | Easier to test |
Complexity | Simpler for basic forms | Better for complex forms |
Dynamic Controls | Not supported well | Fully supported |
1. Template-Driven Forms
Ideal for simple forms with basic validation.
Setup
typescript
// app.module.ts import { FormsModule } from '@angular/forms'; @NgModule({ imports: [FormsModule] })
Basic Form Example
html
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)"> <input name="username" ngModel required #username="ngModel"> <div *ngIf="username.invalid && username.touched"> Username is required </div> <button [disabled]="userForm.invalid">Submit</button> </form>
typescript
onSubmit(form: NgForm) { console.log(form.value); // Form data console.log(form.valid); // Validation status }
Key Features
- Automatic two-way binding withÂ
[(ngModel)]
- Validation through HTML5 attributes + Angular directives
- Template reference variables (
#var
) to access controls
2. Reactive Forms
Better for complex, dynamic forms with custom validation.
Setup
typescript
// app.module.ts import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ imports: [ReactiveFormsModule] })
Basic Form Example
typescript
// component.ts import { FormBuilder, Validators } from '@angular/forms'; export class UserComponent { userForm = this.fb.group({ username: ['', [Validators.required, Validators.minLength(3)]], email: ['', [Validators.required, Validators.email]], address: this.fb.group({ street: [''], city: [''] }) }); constructor(private fb: FormBuilder) {} onSubmit() { console.log(this.userForm.value); } }
html
<form [formGroup]="userForm" (ngSubmit)="onSubmit()"> <input formControlName="username"> <div *ngIf="userForm.get('username').invalid && userForm.get('username').touched"> Username must be at least 3 characters </div> <div formGroupName="address"> <input formControlName="street"> <input formControlName="city"> </div> <button [disabled]="userForm.invalid">Submit</button> </form>
Form Validation Deep Dive
Template-Driven Validation
html
<input name="email" ngModel required email #email="ngModel">
Reactive Form Validation
typescript
this.userForm = this.fb.group({ email: ['', [Validators.required, Validators.email]] });
Custom Validators
typescript
// Shared validator function export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn { return (control: AbstractControl) => { const forbidden = nameRe.test(control.value); return forbidden ? { forbiddenName: { value: control.value } } : null; }; } // Usage username: ['', [ Validators.required, forbiddenNameValidator(/admin/i) ]]
Dynamic Forms
Only possible with Reactive Forms:
typescript
addAlias() { this.aliases = this.userForm.get('aliases') as FormArray; this.aliases.push(this.fb.control('')); }
html
<div formArrayName="aliases"> <h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button> <div *ngFor="let alias of aliases.controls; let i=index"> <input [formControlName]="i"> </div> </div>
Form Best Practices
- Validation Messages: Show only after user interaction (
touched
) - Reusable Components: Create input components with validation messages
- Form Organization: Break large forms into subcomponents
- Debounce Inputs: For search fields (useÂ
valueChanges
 observable) - Form State: Track pristine/dirty status for better UX
Hands-On Exercise
Build a multi-step registration form with:
- Personal info (name, email)
- Address section (street, city, zip)
- Dynamic array for phone numbers
- Cross-field validation (password confirmation)
- Summary review step
What’s Next?
In Part 12, we’ll explore State Management with RxJS – how to manage application state reactively!