Sitemap
Arunkumarvijay

Arunkumarvijay

Angular Dynamic Reactive Form

3 min readAug 12, 2026

--

Configuration → Create FormGroup → Loop controls → Bind HTML → Validate → Submit

1. Requirement

Suppose the fields are not fixed. The backend/configuration gives us:

[
{ name: 'firstName', type: 'text', label: 'First Name', required: true },
{ name: 'email', type: 'email', label: 'Email', required: true },
{ name: 'age', type: 'number', label: 'Age', required: false }
]

Based on this configuration, Angular should dynamically create the form.

Flow

Backend / Configuration
↓
Field Configuration Array
↓
Create FormGroup dynamically
↓
Add FormControl for each field
↓
HTML *ngFor
↓
Render input dynamically
↓
Apply Validators dynamically
↓
User enters data
↓
Form Validation
↓
ngSubmit
↓
Send form.value to API

2. Create the component

import { Component, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup,
Validators
} from '@angular/forms';

@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html'
})
export class DynamicFormComponent implements OnInit {

dynamicForm!: FormGroup;

fields = [
{
name: 'firstName',
label: 'First Name',
type: 'text',
required: true
},
{
name: 'email',
label: 'Email',
type: 'email',
required: true
},
{
name: 'age',
label: 'Age',
type: 'number',
required: false
}
];

constructor(private fb: FormBuilder) {}

ngOnInit(): void {
this.createForm();
}

createForm(): void {

const formGroup: any = {};

this.fields.forEach(field => {

const validators = [];

if (field.required) {
validators.push(Validators.required);
}

if (field.type === 'email') {
validators.push(Validators.email);
}

formGroup[field.name] = ['', validators];

});

this.dynamicForm = this.fb.group(formGroup);
}

submit(): void {

if (this.dynamicForm.invalid) {
this.dynamicForm.markAllAsTouched();
return;
}

console.log(this.dynamicForm.value);
}
}

3. Understand the important part

This is the main dynamic logic:

this.fields.forEach(field => {

const validators = [];

if (field.required) {
validators.push(Validators.required);
}

if (field.type === 'email') {
validators.push(Validators.email);
}

formGroup[field.name] = ['', validators];

});

For example, Angular receives:

{
name: 'email',
type: 'email',
required: true
}

It dynamically creates:

email: ['', [
Validators.required,
Validators.email
]]

So the final FormGroup becomes approximately:

FormGroup {
firstName: FormControl,
email: FormControl,
age: FormControl
}

4. Create HTML dynamically

<form [formGroup]="dynamicForm" (ngSubmit)="submit()">

<div *ngFor="let field of fields">

<label>
{{ field.label }}
</label>

<input
[type]="field.type"
[formControlName]="field.name"
/>

<div
*ngIf="
dynamicForm.get(field.name)?.touched &&
dynamicForm.get(field.name)?.invalid
"
>

<span
*ngIf="dynamicForm.get(field.name)?.hasError('required')"
>
{{ field.label }} is required
</span>

<span
*ngIf="dynamicForm.get(field.name)?.hasError('email')"
>
Please enter a valid email
</span>

</div>

</div>

<button type="submit">
Submit
</button>

</form>

5. How [formControlName] works dynamically

This line is very important:

[formControlName]="field.name"

If:

field.name = 'firstName'

Angular internally treats it like:

formControlName="firstName"

For the next field:

field.name = 'email'

it becomes:

formControlName="email"

Therefore, we don’t need to manually write:

<input formControlName="firstName">
<input formControlName="email">
<input formControlName="age">

The fields are generated dynamically.

6. Validation flow

Suppose the configuration is:

{
name: 'email',
type: 'email',
required: true
}

We create:

Validators.required
Validators.email

So if the user enters:

abc

the form is invalid because:

abc → Invalid email

If the user enters:

abc@gmail.com

then:

Required → Pass
Email → Pass

and the control becomes valid.

7. Submit flow

When the user clicks Submit:

<form
[formGroup]="dynamicForm"
(ngSubmit)="submit()">

Angular calls:

submit()

Then:

if (this.dynamicForm.invalid) {
this.dynamicForm.markAllAsTouched();
return;
}

If invalid, we don’t call the API.

Get Arunkumarvijay’s stories in your inbox

Join Medium for free to get updates from this writer.

If valid:

console.log(this.dynamicForm.value);

Output:

{
"firstName": "Arun",
"email": "arun@gmail.com",
"age": 34
}

This object can then be sent to the backend:

this.http.post('/api/users', this.dynamicForm.value)
.subscribe(response => {
console.log(response);
});

8. Complete medium-level structure

A good real-world structure is:

DynamicFormComponent
│
├── fields configuration
│
├── createForm()
│ │
│ ├── loop fields
│ ├── create FormControl
│ ├── add validators
│ └── create FormGroup
│
├── HTML
│ │
│ ├── *ngFor
│ ├── text input
│ ├── number input
│ ├── email input
│ ├── select
│ └── checkbox
│
├── Validation
│ │
│ ├── required
│ ├── email
│ └── custom validators
│
└── submit()
│
├── check valid
├── get form.value
└── call API

One important point: if the dynamic form contains multiple rows of the same structure — for example, multiple employees, addresses, or tasks — then use FormArray instead of only FormGroup.

Press enter or click to view image in full size
Dynamic Reactive Form Flow Chart

--

--