Menu
×
   ❮     
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR ANGULARJS GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

Angular Reactive Forms Advanced


Advanced reactive forms model complex data with FormGroup/FormArray, combine sync/async validators, and update efficiently with patchValue and updateOn; observe valueChanges/statusChanges for reactive logic.


Reactive Forms Advanced Essentials

  • Structure: Use FormGroup and FormArray to model complex forms.
  • Validation: Combine sync and async validators at control and group levels.
  • Updates: Use patchValue for partial updates; setValue requires the full shape.
import { FormBuilder, Validators, FormArray } from '@angular/forms';

fb.group({
  name: ['', Validators.required],
  tags: fb.array([ fb.group({ label: ['Angular'] }) ])
});

// Add row
(form.get('tags') as FormArray).push(fb.group({ label: [''] }));

Example explained

  • fb.group({...}): Creates a FormGroup with controls and validators.
  • FormArray: Holds an ordered list of controls/groups for dynamic rows.
  • Push a row: (form.get('tags') as FormArray).push(fb.group({ label: [''] })) adds a new group.

Notes:

  • Related: See Forms and Services & DI.
  • Keep form state in a service for reuse across routes.
  • setValue vs patchValue: setValue requires the full shape; patchValue updates a subset.
  • Validate at the control and group levels.

REMOVE ADS


Nested Groups and Arrays

Group related controls for structure and reuse.

Use FormArray for dynamic lists like tags or items. A FormArray is an ordered list of controls whose length can change at runtime.

const tags = fb.array([ fb.group({ label: ['Angular'] }) ]);
tags.push(fb.group({ label: [''] }));

Example explained

  • fb.array([...]): Initializes a FormArray with one FormGroup row.
  • Add row: tags.push(fb.group({ label: [''] })) appends another tag group.

Example

import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormBuilder, Validators, FormArray } from '@angular/forms';
import { JsonPipe, CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, JsonPipe],
  template: `
    <h3>Advanced Reactive Form</h3>
    <form [formGroup]="form" (ngSubmit)="submit()">
      <input placeholder="Name" formControlName="name">
      <div formArrayName="tags">
        <div *ngFor="let t of tags.controls; let i = index" [formGroupName]="i">
          <input placeholder="Tag" formControlName="label">
        </div>
      </div>
      <button type="button" (click)="addTag()">Add Tag</button>
      <button type="submit">Submit</button>
    </form>
    <pre>{{ form.value | json }}</pre>
  `
})
class App {
  fb = new FormBuilder();
  form = this.fb.group({
    name: ['', Validators.required],
    tags: this.fb.array([this.fb.group({ label: ['Angular'] })])
  });
  get tags(): FormArray { return this.form.get('tags') as FormArray; }
  addTag() { this.tags.push(this.fb.group({ label: [''] })); }
  submit() { alert(JSON.stringify(this.form.value)); }
}

bootstrapApplication(App);
<app-root></app-root>

Run Example »

Example explained

  • [formGroup]: Binds the form model to the template.
  • formArrayName: Points to the tags array; each row uses [formGroupName]="i".
  • Getter + push: The tags getter returns the FormArray; addTag() pushes a new group.
  • Submit: Reads form.value, which reflects the nested group/array structure.

Binding tips: Use formArrayName and [formGroupName] for each row to keep bindings aligned.

Consistent shapes: Push groups with the same control shape to a FormArray; avoid mixing primitives and groups.

Dynamic lists: When rendering with *ngFor, use trackBy to keep inputs stable while adding/removing rows.


Validation Strategies

  • Use synchronous validators for most rules; they are fast and simple.
  • Use async validators for server checks/uniqueness; they run after sync validators.
  • Keep validation lean and debounce inputs before async checks.
import { AbstractControl, ValidationErrors } from '@angular/forms';

function banned(value: string[]): (c: AbstractControl): ValidationErrors | null {
  return (c) => value.includes(c.value) ? { banned: true } : null;
}

fb.control('', [Validators.required, banned(['admin'])]);

Example explained

  • Validator factory: banned([...]) returns a function (c: AbstractControl) => ValidationErrors | null.
  • Control-level: Attach sync validators like Validators.required and custom rules to a control.
// Group-level validator and updateOn: 'blur'
import { AbstractControl, ValidationErrors } from '@angular/forms';

function samePassword(group: AbstractControl): ValidationErrors | null {
  const pass = group.get('pass')?.value;
  const confirm = group.get('confirm')?.value;
  return pass === confirm ? null : { mismatch: true };
}

const form = fb.group(
  { pass: [''], confirm: [''] },
  { validators: samePassword, updateOn: 'blur' }
);

Guidelines:

  • Use group-level validators for cross-field rules (e.g., password match).
  • Reduce churn with updateOn: 'blur' | 'submit' (delay validation/value changes until that event) when appropriate.
  • Show errors based on touched/dirty to avoid noisy UX.
  • Async validators should be fast and cancelable; debounce inputs before triggering server checks.


×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
sales@w3schools.com

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
help@w3schools.com

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.