How to create forms dynamically in Angular 7 using FormControl

Krishna Regmi
4 min readFeb 22, 2019

--

I am guessing that you are here because you have a need to create forms dynamically in Angular. We will cover two use cases in the post.

This image has no relevance to the blog post! ;) Photo by Sai Kiran Anagani on Unsplash

UseCase 1

You have a object (or json) describing your form and you need to convert this data into forms in angular. Lets use this simple object for example: We will refer to this object as form template.

const form_template = [
{
"type":"textBox",
"label":"Name",
},
{
"type":"number",
"label":"Age"
},
{
"type":"select",
"label":"favorite book",
"options":["Jane Eyre","Pride and Prejudice","Wuthering Heights"]
}
]
export default form_template

As you can see the list above describes what fields to add. It has three fields, one for text, one for number and last one for the user to select her favorite book. For select field it also has a extra options object since this is required to pupulate options in select field. This json can be made as complex as you need to.

Now, to create a form dynamically in angular, we will need to do following in the ts file. We will use Reactive Form.

  • Create formControl for each item
  • Add all form controls to a form group.
import { Component, OnInit } from '@angular/core'; 
import { FormGroup, FormControl,Validators } from '@angular/forms';
import form_template from 'source of form data';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
styleUrls: ['./dynamic-form.component.css']
})
export class DynamicFormComponent implements OnInit {
myFormGroup:FormGroup;
formTemplate:Any = form_template;
constructor() {}
ngOnInit() {
let group={}
form_template.forEach(input_template=>{
group[input_template.label]=new FormControl('');
})
this.myFormGroup = new FromGroup(group);
}
onSubmit(){
console.log(this.myFormGroup.value);
}
}

In the code block above, during onInit lifecycle stage, we create a group, and for each item in the form template, we convert create a formControl for it, add the formControl to the group object, and finally, create formGroup with this group object.

Next step is to write html that can take the data and convert it into proper forms, and bind each field to the formGroup.

<form [formGroup]="myFormGroup" (ngSubmit)="onSubmit()">
<div *ngFor="let form_elem of formTemplate">
<div [ngSwitch]="form_elem.type">
<div *ngSwitchCase="'textBox'">
<input type="text" formControlName="{{form_elem.label}}"/>
</div>
<div *ngSwitchCase="'number'">
<input type="number" formControlName="{{form_elem.label}}"/>
</div>
<div *ngSwitchCase="'select'">
<select formControlName="{{form_elem.label}}">
<option *ngFor="let opt of form_elem.options">
{{opt}}
</option>
</select>
</div>
</div>
</div>
<input type="submit" value="save"/>
</form>

There you go! Now you should see your dynamic form generate. When Save button is clicked, it’ll print out the value of the formgroup.

UseCase 2

In the second usecase for generating dynamic form, lets say that you are asking the user to recommend their favorite books. However instead of providing a pre-set number of fields where user can enter books, the user can decide for themselves how many books they want to recommend.

Now, there is a way to do this using FormArray. However since its well covered in the docs, I will not be using FormArray. Instead I’ll show you a different way to accomplish it — its more complex, but it’ll help you get an appreciation for actions such as subscribing to formGroup as well as .

Here’s the basic concept:

First, we will create formgroup with formcontrols. We will subscribe to the formGroup and check for any changes in the value of number of books, and create new formcontrols on the fly for the number of books user enters, as well as create html input fields.

Eager to see code? Here it is!

import { Component, OnInit } from '@angular/core'; 
import { FormGroup, FormControl,Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-dynamic-form',
templateUrl: './dynamic-form.component.html',
styleUrls: ['./dynamic-form.component.css']
})
export class DynamicFormComponent implements OnInit {
myFormGroup:FormGroup;
myFormGroupSubs:Subscription;
number_of_books:number[];

constructor() {}
ngOnInit() {
let group={}
group['number_of_books']= new FormControl('');
this.myFormGroup = new FromGroup(group);
this.myFormGroupSubs = this.myFormGroup.valueChanges.subscribe(val=>{
this.handleNumberChange(val);
})
} handleNumberChange(val){
//Try to Remove Control for higher number if any.
const val_plus_one = parseInt(val) + 1
try{
this.myFormGroup.removeControl(val_plus_one.toString())
} catch {}
//Add formControls for 1 - number of books that user enters.
const numbers=Array(val_num ).fill().map((_, idx) => idx + 1 )
numbers.forEach((num)=>{
const fc = new FormControl('');
this.myFormGroup.addControl(num.toString(),fc)
})
this.number_of_books = numbers;
} onSubmit(){
console.log(this.myFormGroup.value);
}
}

Thats all the typescript code. What we did there is that when the value changed, we try to remove existing controls. I am making an assumption that the user will reduce the numbers by one at a time. More robust ways to remove all Controls you don’t need is by iterative over formgroup, and removing controls based on some condition. Because i subscribe to the valueChanges on the formGroup, I can essentially change all my formGroup dynamically.

This code const numbers = Array(val_num).fill().map((_,idx)=> idx+1) will convert a number (eg 3) into a list of numbers from 1 to 3 [1,2,3] . I also keep track of the this list in the state of the component because it’ll come in handy in html code.

<form [formGroup]="myFormGroup" (ngSubmit)="onSubmit()">
Number of books:
<input type="number" min="0" formControlName="number_of_books" />
<div *ngFor="let num in number_of_books"/>
<input type="text" formControlName="{{num}}"/>
</div>
</form>

Thats all you need!

Conclusion

Those are the two usecases of creating dynamically generated forms! I am sure there are plenty more use cases and many different ways of creating dynamic forms. Hope these two examples help you!

--

--

Krishna Regmi

A tech-savvy creator type — leading change through data-driven decisions, and customer-first mentality.