Reactive forms are model-driven: you define the form structure and validation logic in your TypeScript code, then bind it to the template. They are more powerful and scalable than template-driven forms.
1️⃣ Import ReactiveFormsModule
In app.module.ts
:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms'; // ✅ import this
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
ReactiveFormsModule // ✅ add here
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
2️⃣ Define Form in Component
In app.component.ts
:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
myForm!: FormGroup; // declare form
ngOnInit() {
this.myForm = new FormGroup({
name: new FormControl('', [Validators.required, Validators.minLength(3)]),
email: new FormControl('', [Validators.required, Validators.email]),
age: new FormControl('', [Validators.required, Validators.min(18)])
});
}
onSubmit() {
console.log('Form Value:', this.myForm.value);
console.log('Form Valid:', this.myForm.valid);
}
}
3️⃣ Create Template
In app.component.html
:
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div>
<label for="name">Name:</label>
<input id="name" type="text" formControlName="name" />
<div *ngIf="myForm.get('name')?.invalid && myForm.get('name')?.touched" style="color:red">
<span *ngIf="myForm.get('name')?.errors?.['required']">Name is required.</span>
<span *ngIf="myForm.get('name')?.errors?.['minlength']">Name must be at least 3 characters.</span>
</div>
</div>
<div>
<label for="email">Email:</label>
<input id="email" type="email" formControlName="email" />
<div *ngIf="myForm.get('email')?.invalid && myForm.get('email')?.touched" style="color:red">
<span *ngIf="myForm.get('email')?.errors?.['required']">Email is required.</span>
<span *ngIf="myForm.get('email')?.errors?.['email']">Enter a valid email.</span>
</div>
</div>
<div>
<label for="age">Age:</label>
<input id="age" type="number" formControlName="age" />
<div *ngIf="myForm.get('age')?.invalid && myForm.get('age')?.touched" style="color:red">
<span *ngIf="myForm.get('age')?.errors?.['required']">Age is required.</span>
<span *ngIf="myForm.get('age')?.errors?.['min']">You must be at least 18 years old.</span>
</div>
</div>
<button type="submit" [disabled]="myForm.invalid">Submit</button>
</form>
4️⃣ Key Differences (Reactive vs Template-Driven)
Feature | Template-Driven | Reactive |
---|---|---|
Form Model | Defined in template | Defined in TypeScript |
Setup | Simpler, less code | More structured |
Validation | Uses directives (required , email , etc.) | Uses Validators class in TS |
Best For | Simple forms | Complex, dynamic, testable forms |
Testing | Harder | Easier |