Skip to main content

Reactive Forms

Level: Intermediate

ℹ️ What You'll Learn
  • FormControl, FormGroup, FormBuilder
  • Validation in code
  • Dynamic form controls
  • Custom validators
  • Form value and status changes

FormBuilder

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class StudentFormComponent implements OnInit {
form: FormGroup;

constructor(private fb: FormBuilder) {
this.form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
className: ['10', Validators.required]
});
}

ngOnInit() {
// Watch for changes
this.form.valueChanges.subscribe(value => {
console.log('Form value:', value);
});
}

onSubmit() {
if (this.form.valid) {
console.log(this.form.value);
}
}
}

FormBuilder shortcuts form creation.

Template

<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div>
<label>Name:</label>
<input
type="text"
formControlName="name"
>
<span *ngIf="form.get('name')?.hasError('required') && form.get('name')?.touched">
Name required
</span>
</div>

<div>
<label>Email:</label>
<input
type="email"
formControlName="email"
>
<span *ngIf="form.get('email')?.hasError('email')">
Invalid email
</span>
</div>

<select formControlName="className">
<option value="10">10</option>
<option value="11">11</option>
</select>

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

[formGroup]="form" = bind to form. formControlName="name" = bind to control.

Validators

form = this.fb.group({
name: ['', Validators.required],
rollNumber: ['', [Validators.required, Validators.pattern(/^[0-9]+$/)]],
email: ['', [Validators.required, Validators.email]],
className: ['', Validators.required]
});

Built-in validators:

  • required = not empty
  • minLength(n) = min length
  • maxLength(n) = max length
  • email = valid email
  • pattern(regex) = regex match

Custom Validators

function uniqueRollNumber(control: AbstractControl) {
if (!control.value) return null;

// Check if roll number exists
return control.value === '101'
? { uniqueRollNumber: true }
: null;
}

form = this.fb.group({
rollNumber: ['', [Validators.required, uniqueRollNumber]]
});

Return null if valid, error object if invalid.

Dynamic Forms

addSubject() {
const subjects = this.form.get('subjects') as FormArray;
subjects.push(this.fb.control('', Validators.required));
}

removeSubject(index: number) {
const subjects = this.form.get('subjects') as FormArray;
subjects.removeAt(index);
}

form = this.fb.group({
name: ['', Validators.required],
subjects: this.fb.array([
this.fb.control('', Validators.required)
])
});

FormArray = dynamic list of controls.

SMS Form

form = this.fb.group({
rollNumber: ['', [
Validators.required,
Validators.pattern(/^[0-9]{3,}$/)
]],
name: ['', [
Validators.required,
Validators.minLength(3)
]],
email: ['', [
Validators.required,
Validators.email
]],
className: ['10', Validators.required],
parentPhone: ['', [
Validators.required,
Validators.pattern(/^[0-9]{10}$/)
]]
});

onSubmit() {
if (this.form.valid) {
this.studentService.createStudent(this.form.value)
.subscribe(() => alert('Student registered!'));
}
}

Form Value and Status

console.log(form.value); // Form values
console.log(form.status); // VALID or INVALID
console.log(form.get('name')?.value); // Single control value
console.log(form.get('name')?.valid); // Single control valid

form.valueChanges.subscribe(value => {
console.log('Form changed:', value);
});

form.statusChanges.subscribe(status => {
console.log('Status:', status);
});

Key Takeaways

  • FormBuilder = create forms programmatically
  • FormControl = single input
  • FormGroup = group of controls
  • FormArray = dynamic list
  • Validators = built-in validation
  • Custom validators = custom logic
  • valueChanges = react to changes
💡 Backend Developer Tip

Reactive forms more powerful for complex forms. Use Template-driven for simple, Reactive for complex.

⚠️ Reactive Form Issues
  1. Forgetting ReactiveFormsModule — formGroup not available
  2. Using form.name instead of form.get('name') — Wrong access
  3. Not handling null from get() — Null pointer errors
  4. Validators in wrong place — Must be in FormBuilder
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Reactive Forms. Try these prompts:

  • "When use Reactive vs Template-driven forms?"
  • "How do you add dynamic form controls?"
  • "What's difference between value and status?"
  • "Quiz me on FormBuilder"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

nexcoding.in