Angular course · Module 8: Forms

FormArray - Dynamic Fields

1 min read ·

FormArray allows dynamic addition and removal of fields - like adding new techniques to a samurai's arsenal!

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

interface SkillForm {
  name: FormControl<string>;
  level: FormControl<number>;
}

@Component({
  template: `
    <form [formGroup]="samuraiForm" (ngSubmit)="onSubmit()">
      <input formControlName="name" placeholder="Samurai name">

      <h3>Skills</h3>
      <div formArrayName="skills">
        @for (skill of skills.controls; track $index; let i = $index) {
          <div [formGroupName]="i" class="skill-row">
            <input formControlName="name" placeholder="Skill name">
            <input formControlName="level" type="number" placeholder="Level">
            <button type="button" (click)="removeSkill(i)">×</button>
          </div>
        }
      </div>

      <button type="button" (click)="addSkill()">Add skill</button>

      <button type="submit">Save</button>
    </form>
  `
})
export class DynamicFormComponent {
  private fb = inject(FormBuilder);

  samuraiForm = this.fb.group({
    name: ['', Validators.required],
    skills: this.fb.array<FormGroup<SkillForm>>([])
  });

  // Getter for easy access
  get skills(): FormArray<FormGroup<SkillForm>> {
    return this.samuraiForm.get('skills') as FormArray;
  }

  addSkill(): void {
    const skillGroup = this.fb.group({
      name: ['', Validators.required],
      level: [1, [Validators.min(1), Validators.max(10)]]
    });
    this.skills.push(skillGroup);
  }

  removeSkill(index: number): void {
    this.skills.removeAt(index);
  }

  onSubmit(): void {
    if (this.samuraiForm.valid) {
      console.log(this.samuraiForm.value);
      // { name: 'Musashi', skills: [{ name: 'Kendo', level: 5 }, ...] }
    }
  }
}

Initialization with Data

loadSamurai(samurai: Samurai): void {
  // Reset and set simple fields
  this.samuraiForm.patchValue({
    name: samurai.name
  });

  // Clear and add skills
  this.skills.clear();
  samurai.skills.forEach(skill => {
    this.skills.push(this.fb.group({
      name: [skill.name, Validators.required],
      level: [skill.level]
    }));
  });
}

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What is FormArray used for?

  2. 2. Which method do you use to add a new control to the end of a FormArray?

These are 2 of 3 questions for this lesson. Solve the rest in the game.

Hands-on tasks in the game

  • Code editor

    Complete the form with a dynamic list of samurai skills.

  • Horizontal ordering

    Arrange the tokens of the loop header iterating over FormArray controls (one line): @for (skill of skills.controls; track $index)

  • Vertical ordering

    Order the steps for re-populating a FormArray with samurai data (execution order inside the method, line by line).

  • Click in order

    Build the body of the getter that returns a FormArray: return this.form.get('skills') as FormArray. Click the elements in order.

Useful articles