Handling Dynamic Form Controls in Angular: Why `FormRecord` Is a Game Changer

Leader posted 2 min read

Building reactive forms in Angular is one of the most powerful and type-safe ways to handle user input. But what happens when your form structure isn’t fixed — when the form controls depend on dynamic keys that you don’t know at compile time?

If you’ve tried to use FormGroup<T> in such cases, you’ve probably seen TypeScript complaining about missing keys. The solution? Say hello to FormRecord.


The Problem: Dynamic Keys and Type Errors

Let’s say you have a TypeScript interface for an address form:

interface AddressForm {
  street: FormControl<string>;
  city: FormControl<string>;
  zip: FormControl<string>;
}

You can easily create a strongly typed form group:

const address = new FormGroup<AddressForm>({
  street: new FormControl(''),
  city: new FormControl(''),
  zip: new FormControl(''),
});

This works perfectly when all keys (street, city, zip) are known at compile time.

But what if your backend sends different fields dynamically, for example:

{
  address_line1: "123 Main St",
  address_line2: "Apt 4B",
  region: "Downtown"
}

You don’t know the exact field names ahead of time, and that’s where FormGroup<AddressForm> fails.

If you try something like:

const address = new FormGroup<AddressForm>({});

TypeScript throws an error:

❌ Type '{}' is missing the following properties from type 'AddressForm': street, city, zip


The Solution: Use FormRecord

Angular 14+ introduced FormRecord, designed specifically for dynamic sets of form controls.

You can think of it as a FormGroup that doesn’t require predefined keys. It’s a map of FormControls where the keys are strings determined at runtime.

Here’s how to use it:

const address = new FormRecord<FormControl<string | null>>({});

Now you can dynamically add controls:

address.addControl('address_line1', new FormControl('123 Main St'));
address.addControl('address_line2', new FormControl('Apt 4B'));
address.addControl('region', new FormControl('Downtown'));

No compile-time errors.
Full type safety.
Works perfectly with dynamic form data.


When to Use FormGroup vs FormRecord

Use Case Recommended API Description
Static forms (fixed fields known at compile time) FormGroup<T> Great for forms with predictable keys — benefits from strong TypeScript typing.
Dynamic forms (keys vary at runtime) FormRecord<T> Ideal for dynamic forms — allows adding/removing arbitrary controls safely.

Example: Building a Dynamic Settings Form

Here’s a real-world example. Suppose you’re building a settings form where each key/value pair comes from an API:

interface Setting {
  name: string;
  value: string;
}

const settingsData: Setting[] = [
  { name: 'theme', value: 'dark' },
  { name: 'notifications', value: 'enabled' },
  { name: 'language', value: 'en' }
];

You can dynamically build your form like this:

const settingsForm = new FormRecord<FormControl<string>>({});

settingsData.forEach(setting => {
  settingsForm.addControl(setting.name, new FormControl(setting.value));
});

And now you can use it in your template:

<form [formGroup]="settingsForm">
  <div *ngFor="let key of settingsKeys">
    <label>{{ key }}</label>
    <input [formControlName]="key" />
  </div>
</form>

With a simple getter:

get settingsKeys(): string[] {
  return Object.keys(this.settingsForm.controls);
}

Summary

  • FormGroup<T> is strictly typed and great for known structures.
  • FormRecord<T> is flexible and perfect for dynamic or API-driven forms.
  • Both support Angular’s reactive form APIs, validation, and change detection equally well.

If you’re working with dynamic data-driven forms, FormRecord is your new best friend.


✍️ Final Thoughts

Angular’s type-safe forms API has matured beautifully. The addition of FormRecord makes it possible to write flexible, dynamic forms without compromising on type safety — a big win for developer productivity and maintainability.


If you read this far, tweet to the author to show them you care. Tweet a Thanks

1 Comment

1 vote

More Posts

Main approaches to work with dynamic strings in Angular templates.

Sunny - May 14

How to Maintain Fast Load Time in Angular Apps

Sunny - Jun 18

Boost Your Web Apps with Angular: A Beginner’s Guide

test12345 - Aug 26

You Probably Don’t Know How to Do Role-Based Access Control (RBAC) in Angular

Sunny - Aug 29

How DomSanitizer works to prevent Cross-Site Scripting (XSS) attacks in Angular

Sunny - Aug 23
chevron_left