Build Dynamic Forms Using FormArray In Angular
In this article, we will learn how to create a multi-level nested and dynamic FormArray Example. The FormArray is the only way to create nested forms in Angular. We demonstrate how to dynamically add form fields to a two-level nested Form. An employee and his skills will be included in our form. The user will be able to add/remove employees, as well as add/remove any number of skills beneath each employee. If you've never used FormArray before, we recommend reading the Angular FormArray Example.
Angular Nested Formarray
Create an Angular project from scratch. Import the ReactiveFormsModule into the app.module.ts file.
import { ReactiveFormsModule } from '@angular/forms';
Also, add it to the AppModule's NgModule's imports array.
@NgModule({ imports: [BrowserModule, ReactiveFormsModule], declarations: [AppComponent, HelloComponent], bootstrap: [AppComponent] }) export class AppModule {}
Import FormArray
Navigate to AppComponent. Import the Angular Forms Module's FormArray.
import { FormGroup, FormArray, FormBuilder } from '@angular/forms'
Build a Form Model
The first step is to create an empForm Form Model. It simply has one property an employee FormArray.
empForm:FormGroup; constructor(private fb:FormBuilder) { this.empForm=this.fb.group({ employees: this.fb.array([]) , }) }
Employee FormArray
Employees FormArray from the model empForm is returned by this helper function.
employees(): FormArray { return this.empForm.get("employees") as FormArray }
The newEmployee method creates and returns a new employee FormGroup. It has three characteristics. skills, firstname, and lastname FormArray.
newEmployee(): FormGroup { return this.fb.group({ firstName: '', lastName: '', skills:this.fb.array([]) }) }
The technique for adding an employee is next. It makes use of the newEmployee method, which returns the Employee FormGroup and adds it to the array of employees.
addEmployee() { this.employees().push(this.newEmployee()); }
The employee is removed from the array using this method. To remove it, it requires the index position.
removeEmployee(empIndex:number) { this.employees().removeAt(empIndex); }
Skills FormArray
We have a skills list for each employee. As a result, develop a helper method that returns an array of skills from the employee array. The index position of the employee array must be given as an input.
employeeSkills(empIndex:number) : FormArray { return this.employees().at(empIndex).get("skills") as FormArray }
The newSkill function returns a FormGroup of skills. It consists of two fields. Years of experience(exp) and the skill's name
newSkill(): FormGroup { return this.fb.group({ skill: '', exp: '', }) }
The employee's skill is added using the addEmployeeSkill method.
addEmployeeSkill(empIndex:number) { this.employeeSkills(empIndex).push(this.newSkill()); }
Finally, the removeEmployeeSkill method is used to delete an employee's skill.
removeEmployeeSkill(empIndex:number,skillIndex:number) { this.employeeSkills(empIndex).removeAt(skillIndex); }
Employee Forms are accepted using the onSubmit function. From here, you may validate and update the database.
onSubmit() { console.log(this.empForm.value); }
Template
Create a form and use the formGroup directive to connect it to empForm.
<form [formGroup]="empForm" (ngSubmit)="onSubmit()">
We have a variety of employees under empForm. Use the formArrayName directive to connect it to the div element.
<div formArrayName="employees">
Then, using ngFor, cycle over the controls beneath the employees. The index position will be saved in the empIndex local variable if you set let empIndex=index.
<div *ngFor="let employee of employees().controls; let empIndex=index">
In a Form Array, the index is used as the control's name. To tie it to the FormGroup, use the [formGroupName]="empIndex" attribute. The style was created to create a lovely border around the employee.
<div [formGroupName]="empIndex" style="border: 1px solid blue; padding: 10px; width: 600px; margin: 5px;">
Employee's first and last names are input elements. Also, add a button to delete this employee from the array, removeEmployee(empIndex).
{{empIndex}} First Name : <input type="text" formControlName="firstName"> Last Name: <input type="text" formControlName="lastName"> <button (click)="removeEmployee(empIndex)">Remove</button>
Using form, bind the employee's skills to a div. directive ArrayName
<div formArrayName="skills">
ngFor now loops through the employee's skill array.
<div *ngFor="let skill of employeeSkills(empIndex).controls; let skillIndex=index">
skill and experience areas, Additionally, there is a button to delete the skill, which is called removeEmployeeSkill (empIndex,skillIndex)
<div [formGroupName]="skillIndex"> {{skillIndex}} Skill : <input type="text" formControlName="skill"> Exp: <input type="text" formControlName="exp"> <button (click)="removeEmployeeSkill(empIndex,skillIndex)">Remove</button> </div>
Finally, there's a button that lets you add a skill to an employee.
<button type="button" (click)="addEmployeeSkill(empIndex)">Add Skill</button>
Finally, there is a button to add an employee.
<button type="button" (click)="addEmployee()">Add Employee</button>
Now, run the application and see the browser screen looks like below
Updating the Nested forms With Initial Data
It's a little tough to set the initial values in the nested Forms. You must create the form dynamically and then update it using the PatchValue and SetValue methods.
The following code is complete code of the app.component.ts
import { Component, VERSION } from '@angular/core'; import { FormGroup, FormArray, FormBuilder } from '@angular/forms'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { empForm: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.empForm = this.fb.group({ employees: this.fb.array([]) }); } employees(): FormArray { return this.empForm.get('employees') as FormArray; } newEmployee(): FormGroup { return this.fb.group({ firstName: '', lastName: '', skills: this.fb.array([]) }); } addEmployee() { this.employees().push(this.newEmployee()); } removeEmployee(empIndex: number) { this.employees().removeAt(empIndex); } employeeSkills(empIndex: number): FormArray { return this.employees() .at(empIndex) .get('skills') as FormArray; } newSkill(): FormGroup { return this.fb.group({ skill: '', exp: '' }); } addEmployeeSkill(empIndex: number) { this.employeeSkills(empIndex).push(this.newSkill()); } removeEmployeeSkill(empIndex: number, skillIndex: number) { this.employeeSkills(empIndex).removeAt(skillIndex); } onSubmit() { console.log(this.empForm.value); } }
The following code is complete code of the app.component.html
<h1>Angular Nested FormArray / Dynamic FormArray</h1> <form [formGroup]="empForm" (ngSubmit)="onSubmit()"> <div formArrayName="employees"> <div *ngFor="let employee of employees().controls; let empIndex=index"> <div [formGroupName]="empIndex" style="border: 1px solid blue; padding: 10px; width: 600px; margin: 5px;" > {{empIndex}} First Name : <input type="text" formControlName="firstName" /> Last Name: <input type="text" formControlName="lastName" /> <button (click)="removeEmployee(empIndex)">Remove</button> <div formArrayName="skills"> <div *ngFor="let skill of employeeSkills(empIndex).controls; let skillIndex=index" > <div [formGroupName]="skillIndex"> {{skillIndex}} Skill : <input type="text" formControlName="skill" /> Exp: <input type="text" formControlName="exp" /> <button (click)="removeEmployeeSkill(empIndex,skillIndex)"> Remove </button> </div> </div> </div> <button type="button" (click)="addEmployeeSkill(empIndex)"> Add Skill </button> </div> </div> <button type="button" (click)="addEmployee()">Add Employee</button> </div> </form> {{this.empForm.value | json}}
Conclusion
In this article, We covered how to create many tiers of nested forms in Angular.
I hope this article helps you and you will like it.👍
If you have any doubt or confusion then free to ask in comment section.