On this post, I aim to explain the thought behind the term “dynamic” forms in Angular.

Dynamic forms is actually a pattern (not another API to build forms as some may think) in which we build a form based on meta description, and we use the reactive form API in order to achieve it.

The Reactive Forms API gives us some really useful tools to build the form, access its data model via accessing the form-group controls (directly via controls or using a getter of a certain control) etc and define its validators all via the code.

The API provides the following:

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

Short review:

new FormGroup will create an instance of the form group.

new FormControl will create an instance of the control — to be given to a form group.

Validators — lots of options of validations to be set on a control.

FormBuilder — the static way to create the FormGroup and associated controls (and validations) in a relatively shorter syntax.

FormArray — enable to access control as a FormArray to dynamically create a set of controls, this is a bit more hard to catch so I added the below code snippet:

@Input() myfromGroup: FormGroup;



addItem() {

this.repeatControls = this.myfromGroup.get('repeatControls') as FormArray;

this.repeatControls.push(this.createItem());

}



createItem() {

return this.formBuilder.array(

[this.formBuilder.group(this.myfromGroup.controls.repeatControls)],

);

}

(In addition, there are the correlated directives such as formControlName, formGroup, formArrayName)

On top of that API the term “Dynamic forms” was built,

“ It may be more economical to create the forms dynamically, based on metadata that describes the business object model.”

An example to meta description:

nameDescription: {

name: {

type: 'text',

value: 'Type policy name',

label: 'name',

validation: {

required: true,

},

},

description: {

type: 'text',

value: 'Type description for policy',

label: 'description',

validation: {

required: false,

},

},

notes: {

type: 'text',

value: 'Write a comment to explain what changes',

label: 'notes',

validation: {

required: false,

},

}, cancel: {

type: 'button',

label: 'Cancel',

},

OK: {

type: 'button',

label: 'OK',

}

},

A playable example:

As for more complex forms containing inner forms:

As I came to realize recently, creating a formGroup with nested formGroups is not a good architecture pattern.

In case of nested forms — make the inner form a control by using the ControlValueAccessor API and reactively update the form group upon a change, IMO that’s the Angular way of handling nested forms, it is much more elegant and simple approach, in general, it is better to keep structures as flat as possible so the complexity of the iteration will stay linear:

DO:

createForm(descriptor): FormGroup {

const _simpleFormGroup: FormGroup | any = {};

for (const _control of Object.keys(descriptor)) {

let control: FormControl;

if ( this.descriptor[_control].value ) {

if (this.descriptor[_control].Validators) {

control = this.formBuilder.control(this.descriptor[_control].value, {

validators: this.descriptor[_control].Validators,

});

} else {

control = this.formBuilder.control(this.descriptor[_control].value);

}

}

_simpleFormGroup[_control] = control;

}

return this.formBuilder.group(_simpleFormGroup);

}

If for any reason you choose to have a nested form structure here’s my solution:

DONT:

createMainForm(desc) {

const mainFormGroup = {};

for (const nestedGroup of Object.keys(desc)) {

const nestedFormGroup = {};

let formControl;

for (const control of Object.keys(desc[nestedGroup])) {

formControl = this.formBuilder.control(desc[nestedGroup][control].value);

nestedFormGroup[control] = formControl;

}

mainFormGroup[nestedGroup] = this.formBuilder.group(nestedFormGroup);

}

return this.formBuilder.group(mainFormGroup);

}

I hope I managed to explain it on a simple way, it’s really not complicated once you understand the reactive forms API, using it together with ngSwitch, ngFor we can do some really cool generic stuff.

Reference: