Nested FormArray Example Add Form Fields Dynamically

In this guide, we will show you how to build a multi-level nested FormArray Example. We show you how to add form fields dynamically in a 2 level nested Form. Our Form will consist of an employee and his skills. The user will be able to add/remove employee’s and under each employee, you can add/remove any number of skills. If you have not used FormArray, then we suggest you read the FormArray Example in Angular.

Applies to: Angular 2 to the latest edition of i.e. Angular 8. Angular 9, Angular 10

Import FormArray

Import the FormArray from the Angular Forms Module.

Build a Form Model

The First task is to build a Form Model empForm. It has only one property a FormArray of employees.

Employee FormArray

Helper method, which returns the employees FormArray from the model empForm

The newEmployee method creates a new employee FormGroup and returns it. It has three properties. firstName, lastName and skills FormArray.

Next, the method to add an employee. It uses the newEmployee method which returns the Employee FormGroup and ads it to employees array.

Method to remove the employee form the array. It needs the index position to remove it.

Skills FormArray

Under each employee, we have skills array. Hence create helper method which returns a skills array from the employee array. We need to pass the index position of the employee array as argument.

newSkill method returns a skill FormGroup. It has two fields. Name of the skill and years of exp

addEmployeeSkill method the skill to employee.

Finally, removeEmployeeSkill method, which removes the skill of an employee.

Template

Create a <form> and bind it to empForm using formgroup directive

Under empForm we have employees array. Bind it to the div element using formArrayName directive

Next, loop through the controls under the employees using ngFor. let empIndex=index will save the index position in the empIndex local variable.

The index is used as the name of the control in a Form Array. Hence use the [formGroupName]="empIndex" to bind it to the FormGroup. The style exists to show a nice border around employee

Input element for employee’s firstNamelastName. Also, place a button removeEmployee(empIndex) to remove this employee from the array.

Bind the skills of the empoyee to a div using formArrayName directive

ngFor, now loops through the skills array of the employee.

input fields for skill and exp, Also button to remove the skill, which calls the removeEmployeeSkill(empIndex,skillIndex)

Finally a button the add the skill to the employee

Finally a button the add the employee


Best Angular Books
The Top 8 Best Angular Books, which helps you to get started with Angular  

Nested FormArray Example. Add Form Fields Dynamically
Nested FormArray Example. Add Form Fields Dynamically

Source Code

app.component.ts

app.component.html

You can download the source code for this and entire Angular Forms Tutorial from GitHub

Reference

Summary

In this tutorial, we learned how to create Multiple levels of nested forms using FormArray.

Complete List of Angular Forms Tutorials

  1. Angular Forms Tutorial: Fundamental & Concepts
  2. Template Driven Forms in Angular
  3. Set Value in Template Driven forms in Angular
  4. Reactive Forms in Angular
  5. FormBuilder in Reactive Forms
  6. SetValue & PatchValue in Angular
  7. StatusChanges in Angular Forms
  8. ValueChanges in Angular Forms
  9. FormControl
  10. FormGroup
  11. FormArray Example
  12. Build Dynamic or Nested Forms using FormArray
  13. Validations in Reactive Forms in Angular
  14. Custom Validator in Reactive Forms
  15. Passing Parameter to Custom Validator in Reactive Forms
  16. Inject Service into Custom Validator
  17. Validation in Template Driven Forms
  18. Custom Validator in Template Driven Forms

10 thoughts on “Nested FormArray Example Add Form Fields Dynamically”

  1. Hello,

    I’ve tried this out and just have a question, for the new employees,

    newEmployee(): FormGroup {
    return this.fb.group({
    firstName: ”,
    lastName: ”,
    skills:this.fb.array([])
    })
    }

    would it be possible to only return the control rather than have the tag? so instead of having: {“firstName”: “Tony”}. it’s instead going to just be {“Tony”}

    1. Oops sorry, meant this for skills, any idea how to do that? Basically for skills, it’s just an array of skills no experience

      example: {“Photoshop”, “Illustrator”}

  2. Very interesting. I was having problems to render nested form-arrays in the template, but you helped me to solve them. Thanks!

  3. great walkthrough. Thank you. I was wondering how to add infinite employee forms to each other. I have a list of commands, and each can have a subcommands. Subcommands can also have subcommands as well until the flow is met. Any suggestions on how it can be done? thanks again

    1. “infinite” depth sub-commands where depth is unknown would be tricky. I think you should go with Nested Components along with the Nested FormArray.

      I hope this helps

      1. I don’t expect the number of sun-commands to be very large. However, I also can’t determine the number of sub-commands. Thus, having the option to add as many sub-commands as it’s needed is quite what i need. I don’t see the use of sub components since i need to use the exact same form. What do you think? any thoughts on how that can be implemented.

        Thanks,

  4. Thank you very much for sharing this since it has been very difficult for me to program it, implement your code in my project and everything works fine except the edit part.

    When I receive the values ​​in the from, how should I implement the loadResource ???

    Actually I have it as follows and in its code does not load the skills :::

    this.route.paramMap.pipe(
    switchMap(params => this.resourceService.getById(params.get(‘id’))),
    )
    .subscribe(
    (resource) => {
    this.resource = resource;
    console.log(‘resource: ‘ + JSON.stringify(resource));
    // this.resourceForm.patchValue(resource); // binds loaded resource data to resourceForm
    // tslint:disable-next-line: forin
    for (const i in this.resource) {
    const control = this.resourceForm.controls[i];
    if (control != null && i !== ‘constructor’) {
    const objV = this.resource[i];
    if (Array.isArray(objV)) {
    const formArray = this.resourceForm.controls[i] as FormArray;
    formArray.removeAt(0);
    // tslint:disable-next-line: forin
    for (const key in objV) {
    const element = objV[key];
    // tslint:disable-next-line: no-shadowed-variable
    const formArray = this.resourceForm.controls[i] as FormArray;
    formArray.push(this.formBuilder.group(element));
    }
    } else
    this.resourceForm.controls[i].setValue(objV);
    }
    }

    if (execFunctionAfterLoadResource)
    execFunctionAfterLoadResource.call();
    },
    () => {
    this.toastrService.danger(
    ‘Error inserver….’,
    );
    });
    }

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top