Mastering Angular Reactive Forms: FormBuilder, Validators, and Dynamic Controls
A comprehensive guide to mastering Angular Reactive Forms with FormBuilder, validation techniques, async validators, dynamic controls, and integration with Angular Material.
1. FormBuilder – Simplifying Form Creation
FormBuilder makes it easier to create forms by reducing boilerplate code.
Without FormBuilder (Verbose)
1
2
3
4
5
6
import { FormGroup, FormControl, Validators } from '@angular/forms';
this.userForm = new FormGroup({
name: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email])
});
With FormBuilder (Shorter & Cleaner)
1
2
3
4
5
6
7
8
import { FormBuilder } from '@angular/forms';
constructor(private fb: FormBuilder) {}
this.userForm = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
Use Case: Use FormBuilder for cleaner, more readable code.
2. Listening to Status Changes
statusChanges
lets you detect when the form becomes valid, invalid, pending, or disabled.
Example: Listen to Form Status Changes
1
2
3
this.userForm.statusChanges.subscribe(status => {
console.log('Form status changed:', status);
});
Possible values:
- VALID
- INVALID
- PENDING
- DISABLED
Use Case: Show submit button only when the form is valid.
3. Custom Async Validator with HTTP API
Validating data with a backend API call (e.g., checking username availability).
Example: Check if Email Exists (API Call)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { AbstractControl, ValidationErrors } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map, debounceTime, switchMap } from 'rxjs/operators';
constructor(private http: HttpClient) {}
emailValidator(control: AbstractControl): Observable<ValidationErrors | null> {
return control.valueChanges.pipe(
debounceTime(500), // Wait before making API call
switchMap(email =>
this.http.get(`https://api.example.com/check-email/${email}`).pipe(
map((response: any) => (response.exists ? { emailTaken: true } : null))
)
)
);
}
Use Case: Checking unique fields like email, username, phone numbers, etc.
4. Multi-Step Form with Reactive Forms
Large forms can be split into multiple steps, where each step is a different FormGroup
.
Example: Two-Step Registration Form
1
2
3
4
5
6
7
8
9
this.step1 = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
this.step2 = this.fb.group({
password: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', Validators.required]
});
Move Between Steps
1
2
3
4
5
nextStep() {
if (this.step1.valid) {
this.currentStep = 2;
}
}
Use Case: Signup flows, checkout processes, surveys, and complex forms.
5. Dynamic Form Controls with FormArray
Add fields dynamically, like adding multiple phone numbers or skills.
Example: Add/Remove Phone Numbers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
this.userForm = this.fb.group({
phones: this.fb.array([this.fb.control('')])
});
get phones() {
return this.userForm.get('phones') as FormArray;
}
addPhone() {
this.phones.push(this.fb.control(''));
}
removePhone(index: number) {
this.phones.removeAt(index);
}
HTML
1
2
3
4
5
6
7
<div formArrayName="phones">
<div *ngFor="let phone of phones.controls; let i = index">
<input [formControlName]="i" placeholder="Phone Number" />
<button (click)="removePhone(i)">Remove</button>
</div>
</div>
<button (click)="addPhone()">Add Phone</button>
Use Case: Adding dynamic items like multiple addresses, emails, phone numbers, or skills.
6. Disable or Enable Form Controls Dynamically
Based on conditions, we can enable or disable form controls dynamically.
Example: Enable/Disable Based on Checkbox
1
2
this.userForm.get('email')?.disable(); // Disables the email field
this.userForm.get('email')?.enable(); // Enables the email field
Use Case: Make fields editable only when needed (e.g., “Edit Profile” mode).
7. Submitting & Sending Form Data to API
After form validation, we submit data to a backend.
Example: Submit Form to API
1
2
3
4
5
6
submitForm() {
if (this.userForm.valid) {
this.http.post('https://api.example.com/submit', this.userForm.value)
.subscribe(response => console.log('Form Submitted:', response));
}
}
Use Case: Login, registration, profile updates, and form submissions.
8. Reactive Forms with Material UI
Using Angular Material components with ReactiveForms.
Example: Material Input & Checkbox
1
2
3
4
5
6
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput formControlName="name" />
</mat-form-field>
<mat-checkbox formControlName="subscribe">Subscribe to Newsletter</mat-checkbox>
Use Case: Better UI/UX with Material Design.
9. Prefilling Form with Data (Edit Mode)
When updating a form, we need to load existing user data.
Example: Prefill User Data
1
2
3
4
this.userForm.patchValue({
name: 'John Doe',
email: 'john@example.com'
});
Use Case: Edit user profile, update settings, etc.
10. Reset Form After Submission
After submitting, we may need to reset the form.
Example: Reset Form After Submit
1
this.userForm.reset(); // Resets all values