typescript 56 lines · 8 steps

Building dynamic reactive forms in Angular

Generate a validated reactive form at runtime from a schema array instead of hardcoding controls.

Explained by highlit
1import { Component, Input, OnInit } from '@angular/core';
2import { FormBuilder, FormGroup, FormArray, FormControl, Validators, ValidatorFn } from '@angular/forms';
3 
4interface FieldSchema {
5 key: string;
6 label: string;
7 type: 'text' | 'number' | 'email' | 'checkbox' | 'select';
8 required?: boolean;
9 min?: number;
10 max?: number;
11 options?: { value: string; label: string }[];
12}
13 
14@Component({
15 selector: 'app-dynamic-form',
16 templateUrl: './dynamic-form.component.html',
17})
18export class DynamicFormComponent implements OnInit {
19 @Input() schema: FieldSchema[] = [];
20 
21 form!: FormGroup;
22 
23 constructor(private fb: FormBuilder) {}
24 
25 ngOnInit(): void {
26 this.form = this.fb.group({
27 fields: this.fb.array(this.schema.map((field) => this.buildControl(field))),
28 });
29 }
30 
31 get fields(): FormArray<FormControl> {
32 return this.form.get('fields') as FormArray<FormControl>;
33 }
34 
35 private buildControl(field: FieldSchema): FormControl {
36 const validators: ValidatorFn[] = [];
37 if (field.required) {
38 validators.push(field.type === 'checkbox' ? Validators.requiredTrue : Validators.required);
39 }
40 if (field.type === 'email') validators.push(Validators.email);
41 if (field.min != null) validators.push(Validators.min(field.min));
42 if (field.max != null) validators.push(Validators.max(field.max));
43 
44 const initial = field.type === 'checkbox' ? false : '';
45 return this.fb.control(initial, validators);
46 }
47 
48 submit(): { key: string; value: unknown }[] {
49 this.form.markAllAsTouched();
50 if (this.form.invalid) return [];
51 return this.schema.map((field, i) => ({
52 key: field.key,
53 value: this.fields.at(i).value,
54 }));
55 }
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A schema-driven form lets you describe fields as data and generate controls, so new fields need no template or class changes.
  2. 2Building validators conditionally from each field's metadata keeps validation rules colocated with the field definition.
  3. 3Marking a form as touched before checking validity ensures error messages surface when the user attempts submission.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building dynamic reactive forms in Angular — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code