Skip to the solution
MediumFrontend8 min read

What is a FormArray?

339 views

The problem

Question presented to candidate: "An event-booking form lets the user add any number of guests, each with a name and a meal choice, and remove them again. What is a FormArray, and how would you build this with it?"

What a strong answer should cover:

  • A FormArray is a reactive-forms container that holds an ordered list of controls addressed by index, for repeating fields whose count is not known up front.
  • Items can be FormControls or whole FormGroups; the array's value is an array of their values, and its status aggregates the children (one invalid row makes the array invalid).
  • Mutation API: push, insert, removeAt (accepts negative indexes), setControl, clear, at, length.
  • Template wiring: formArrayName on a wrapper, [formGroupName]="i" per row, rendered with @for and a stable track (the control object).
  • Typed arrays (FormArray<FormGroup<{...}>>), plus validating the list itself: Validators.required means "at least one item", while Validators.minLength(1) does not catch an empty array.
  • Contrast with FormGroup (fixed named keys) and FormRecord (dynamic string keys).

Clarifying questions expected:

  • "Is there a minimum or maximum number of guests?"
  • "Do rows need stable identity, for example to keep focus or animations when one in the middle is removed?"

Code / implementation expected: Yes β€” a FormArray of FormGroups with add and remove buttons, showing the value after each change.

  • forms
  • formarray

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

8 min read Β· 10 sections

Read the code

AngularA guest list built with a typed FormArray of FormGroups: add, edit and remove rows
import { Component, signal } from "@angular/core";
import { FormArray, FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";

type Guest = FormGroup<{ name: FormControl<string>; meal: FormControl<string> }>;

@Component({
  selector: "app-root",
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form">
      <div formArrayName="guests">
        @for (guest of guests.controls; track guest; let i = $index) {
          <div class="row" [formGroupName]="i">
            <input class="name" formControlName="name" placeholder="name" />
            <input class="meal" formControlName="meal" placeholder="meal" />
            <button type="button" class="remove" (click)="remove(i)">x</button>
          </div>
        }
      </div>
      <button type="button" id="add" (click)="add()">Add guest</button>
    </form>
    <p id="out">{{ out() }}</p>
  `,
})
export class AppComponent {
  form = new FormGroup({
    event: new FormControl("Launch party", { nonNullable: true }),
    // A FormArray holds an ordered list of controls: here, one FormGroup per guest.
    // Validators.required on an array means "at least one item" (minLength(1) would NOT catch an empty array).
    guests: new FormArray<Guest>([this.guest("Ada", "veg")], { validators: Validators.required }),
  });
  out = signal("");

  constructor() {
    this.form.valueChanges.subscribe(() => this.snapshot());
    this.snapshot();
  }

  get guests() {
    return this.form.controls.guests;
  }

  guest(name = "", meal = "standard"): Guest {
    return new FormGroup({
      name: new FormControl(name, { nonNullable: true, validators: Validators.required }),
      meal: new FormControl(meal, { nonNullable: true }),
    });
  }

  add() {
    this.guests.push(this.guest());
  }

  remove(i: number) {
    this.guests.removeAt(i);
  }

  snapshot() {
    this.out.set("length=" + this.guests.length + " status=" + this.form.status + " value=" + JSON.stringify(this.form.value.guests) + " arrayErrors=" + JSON.stringify(this.guests.errors));
  }
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 68 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

How do you handle internationalization (i18n) in Angular?

Continue