Skip to main content
Angular Basics
CHAPTER 13 Beginner

Reactive Forms

Updated: May 18, 2026
5 min read

# CHAPTER 13

Reactive Forms

1. Chapter Introduction

Reactive Forms move all form definition and validation logic into TypeScript, giving developers a programmable, testable, and scalable approach to form handling. They are built around observable streams, making them ideal for dynamic forms where fields can be added or removed at runtime, and for complex validation scenarios.

2. Learning Objectives

  • Import and use ReactiveFormsModule.
  • Create forms with FormBuilder.
  • Apply synchronous and async validators.
  • Build dynamic form fields with FormArray.
  • Create a custom validator function.

3. Creating a Reactive Form

survey.component.ts
1234567891011121314151617181920212223242526
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-survey',
  templateUrl: './survey.component.html'
})
export class SurveyComponent {
  surveyForm: FormGroup;

  constructor(private fb: FormBuilder) {
    // FormBuilder creates the form structure concisely
    this.surveyForm = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      email: ['', [Validators.required, Validators.email]],
      age: [null, [Validators.required, Validators.min(18), Validators.max(100)]],
      feedback: ['']
    });
  }

  onSubmit(): void {
    if (this.surveyForm.valid) {
      console.log(this.surveyForm.value);
    }
  }
}
html
123456789101112131415
<!-- survey.component.html -->
<form [formGroup]="surveyForm" (ngSubmit)="onSubmit()">

  <input formControlName="name" placeholder="Your Name" />
  <div *ngIf="surveyForm.get(&#039;name')?.invalid && surveyForm.get('name')?.touched">
    <span *ngIf="surveyForm.get(&#039;name')?.errors?.['required']">Required.</span>
    <span *ngIf="surveyForm.get(&#039;name')?.errors?.['minlength']">Min 3 chars.</span>
  </div>

  <input formControlName="email" placeholder="Email" />
  <input type="number" formControlName="age" placeholder="Age" />
  <textarea formControlName="feedback" placeholder="Feedback"></textarea>

  <button type="submit" [disabled]="surveyForm.invalid">Submit</button>
</form>

4. FormArray — Dynamic Fields

FormArray allows you to manage a dynamically growing list of form controls:
typescript
123456789101112131415161718192021
import { FormArray, FormControl } from &#039;@angular/forms&#039;;

export class DynamicFormComponent {
  form = this.fb.group({
    skills: this.fb.array([this.fb.control(&#039;&#039;)])
  });

  constructor(private fb: FormBuilder) {}

  get skills(): FormArray {
    return this.form.get(&#039;skills&#039;) as FormArray;
  }

  addSkill(): void {
    this.skills.push(this.fb.control(&#039;&#039;));
  }

  removeSkill(index: number): void {
    this.skills.removeAt(index);
  }
}
html
1234567
<div formArrayName="skills">
  <div *ngFor="let skill of skills.controls; let i = index">
    <input [formControlName]="i" placeholder="Skill {{ i + 1 }}" />
    <button (click)="removeSkill(i)">Remove</button>
  </div>
</div>
<button (click)="addSkill()">+ Add Skill</button>

5. Custom Validators

typescript
12345678910111213
import { AbstractControl, ValidationErrors } from &#039;@angular/forms&#039;;

// Custom validator function
export function passwordStrengthValidator(control: AbstractControl): ValidationErrors | null {
  const value: string = control.value || &#039;&#039;;
  const hasUpperCase = /[A-Z]/.test(value);
  const hasNumber = /[0-9]/.test(value);

  if (!hasUpperCase || !hasNumber) {
    return { weakPassword: true }; // Returns error object if invalid
  }
  return null; // Returns null if valid
}
typescript
12
// Using the custom validator
password: [&#039;&#039;, [Validators.required, passwordStrengthValidator]]
html
123
<span *ngIf="form.get(&#039;password')?.errors?.['weakPassword']">
  Password must contain an uppercase letter and a number.
</span>

6. Watching Value Changes

typescript
1234567891011
ngOnInit(): void {
  // React to any value change in the form
  this.surveyForm.valueChanges.subscribe(values => {
    console.log(&#039;Form changed:&#039;, values);
  });

  // React to a specific control's changes
  this.surveyForm.get(&#039;email&#039;)?.valueChanges.subscribe(email => {
    console.log(&#039;Email changed to:&#039;, email);
  });
}

7. Common Mistakes

  • Forgetting [formGroup] on the <form> tag: The HTML form must be bound to the TypeScript FormGroup using [formGroup]="myForm".
  • Using [(ngModel)] alongside formControlName: These two approaches conflict. In Reactive Forms, use only formControlName.

8. MCQs with Answers

Question 1

Which module must be imported for Reactive Forms?

Question 2

What service provides a shorthand syntax for creating FormGroups?

Question 3

How is a Reactive Form bound to an HTML <form> element?

Question 4

How is an individual input bound to a control in a Reactive Form?

Question 5

What class manages a dynamic list of form controls?

Question 6

What does a custom validator return if the input is VALID?

Question 7

What does a custom validator return if the input is INVALID?

Question 8

How do you programmatically set a form field's value in a Reactive Form?

Question 9

Which Reactive Form feature allows you to subscribe to real-time value changes?

Question 10

What is the benefit of Reactive Forms over Template-Driven Forms?

9. Interview Questions

  • Q: How would you implement a "confirm password" cross-field validator in a Reactive Form?
  • Q: What is FormArray and when would you use it?

10. Summary

Reactive Forms give developers surgical control over form construction, validation, and state management entirely in TypeScript. The FormBuilder shorthand, custom validators, and valueChanges Observable make them the professional's choice for any non-trivial form in a production Angular application.

11. Next Chapter Recommendation

Our app needs real data from the internet. In Chapter 14: Angular HTTP Client and APIs, we will learn how to use Angular's HttpClient to make GET, POST, PUT, and DELETE requests and integrate with real REST APIs.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·