Master reactive forms in Angular with FormControl, FormGroup, FormArray, and advanced validation techniques.
Master reactive forms in Angular with FormControl, FormGroup, FormArray, and advanced validation techniques.
Learn about Setting Up Reactive Forms.
1import { Component, OnInit } from '@angular/core';
2import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';
3
4@Component({
5 selector: 'app-login',
6 standalone: true,
7 imports: [ReactiveFormsModule],
8 template: \`
9 <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
10 <input formControlName="email" placeholder="Email">
11 <input formControlName="password" type="password">
12 <button type="submit" [disabled]="loginForm.invalid">Login</button>
13 </form>
14 \`
15})
16export class LoginComponent implements OnInit {
17 loginForm = new FormGroup({
18 email: new FormControl('', [Validators.required, Validators.email]),
19 password: new FormControl('', [Validators.required, Validators.minLength(8)])
20 });
21
22 ngOnInit() {
23 // Listen to value changes
24 this.loginForm.valueChanges.subscribe(value => {
25 console.log('Form value:', value);
26 });
27 }
28
29 onSubmit() {
30 if (this.loginForm.valid) {
31 console.log(this.loginForm.value);
32 }
33 }
34}Learn about FormBuilder Service.
1import { Component, inject } from '@angular/core';
2import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
3
4@Component({
5 selector: 'app-register',
6 standalone: true,
7 imports: [ReactiveFormsModule],
8 template: \`<form [formGroup]="registerForm" (ngSubmit)="onSubmit()">...</form>\`
9})
10export class RegisterComponent {
11 private fb = inject(FormBuilder);
12
13 registerForm = this.fb.group({
14 username: ['', [Validators.required, Validators.minLength(3)]],
15 email: ['', [Validators.required, Validators.email]],
16 password: ['', [Validators.required, Validators.minLength(8)]],
17 confirmPassword: ['', Validators.required],
18 address: this.fb.group({
19 street: [''],
20 city: ['', Validators.required],
21 zipCode: ['', [Validators.required, Validators.pattern(/^\\d{5}$/)]]
22 })
23 });
24
25 onSubmit() {
26 if (this.registerForm.valid) {
27 console.log(this.registerForm.value);
28 }
29 }
30}Learn about Custom Validators.
1import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';
2
3// Custom validator function
4export function passwordMatchValidator(): ValidatorFn {
5 return (control: AbstractControl): ValidationErrors | null => {
6 const password = control.get('password');
7 const confirmPassword = control.get('confirmPassword');
8
9 if (!password || !confirmPassword) {
10 return null;
11 }
12
13 return password.value === confirmPassword.value ? null : { passwordMismatch: true };
14 };
15}
16
17// Usage
18registerForm = this.fb.group({
19 password: ['', Validators.required],
20 confirmPassword: ['', Validators.required]
21}, { validators: passwordMatchValidator() });Learn about FormArray for Dynamic Fields.
1import { Component, inject } from '@angular/core';
2import { FormBuilder, FormArray, ReactiveFormsModule } from '@angular/forms';
3
4@Component({
5 selector: 'app-skills',
6 standalone: true,
7 imports: [ReactiveFormsModule, CommonModule],
8 template: \`
9 <form [formGroup]="profileForm">
10 <div formArrayName="skills">
11 @for (skill of skills.controls; track $index) {
12 <input [formControlName]="$index" placeholder="Skill {{ $index + 1 }}">
13 }
14 </div>
15 <button type="button" (click)="addSkill()">Add Skill</button>
16 <button type="button" (click)="removeSkill($index)">Remove</button>
17 </form>
18 \`
19})
20export class SkillsComponent {
21 private fb = inject(FormBuilder);
22
23 profileForm = this.fb.group({
24 skills: this.fb.array([
25 this.fb.control('')
26 ])
27 });
28
29 get skills() {
30 return this.profileForm.get('skills') as FormArray;
31 }
32
33 addSkill() {
34 this.skills.push(this.fb.control(''));
35 }
36
37 removeSkill(index: number) {
38 this.skills.removeAt(index);
39 }
40}Learn about Async Validators.
1import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms';
2import { Observable, of, delay, map } from 'rxjs';
3
4export function usernameAvailableValidator(): AsyncValidatorFn {
5 return (control: AbstractControl): Observable<ValidationErrors | null> => {
6 if (!control.value) {
7 return of(null);
8 }
9
10 // Simulate API call
11 return of(control.value).pipe(
12 delay(500),
13 map(username => {
14 const taken = ['admin', 'user', 'test'];
15 return taken.includes(username) ? { usernameTaken: true } : null;
16 })
17 );
18 };
19}
20
21// Usage
22username: new FormControl('',
23 [Validators.required],
24 [usernameAvailableValidator()]
25);Use reactive forms for complex forms with dynamic validation
Use FormBuilder for cleaner form creation syntax
Create reusable custom validators for business logic
Use async validators for server-side validation
Unsubscribe from form valueChanges in ngOnDestroy
Use FormArray for dynamic lists of form controls
Leverage RxJS operators for complex form logic
Use patchValue() for partial updates, setValue() for complete updates
Test forms by creating form instances without components
Reactive forms use ReactiveFormsModule and provide programmatic control over form state
FormControl, FormGroup, and FormArray are the building blocks of reactive forms
FormBuilder service provides a concise syntax for creating forms
Custom validators enable reusable business logic validation
Async validators enable server-side validation without blocking the UI
FormArray enables dynamic form fields that can be added/removed at runtime
valueChanges observable enables reactive programming patterns with forms
Nested FormGroups enable complex hierarchical form structures
Reactive forms are more testable and scalable than template-driven forms
Your progress is saved in this browser, on this device only.