Search code examples
javascriptangularangular-formsangular-formbuilder

angular dynamic forms add nested form arrays


I am trying to create a dynamic form where you can add forms dynamically, and sub forms dynamically. For example:

+ user1  
--- + color1  
--- + color2  
--- + color3  
+ user2  
--- + color1  
--- + color2  
--- + color3  
+ user 3  
--- + color1

Where as you can add as many users as you want, and you can add as many colors as you want to each user. I can get the first array to work (users), but not sure about the nested array (colors). I have it set up so it loads a user and a color at the beginning. Here is my code I have so far:

export class FormsComponent implements OnInit {

  myForm: FormGroup;

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.myForm = this.fb.group({
      email: '',
      users: this.fb.array([])
    })
  }

  get userForms() {
    return this.myForm.get('users') as FormArray
  }

  get colorForms() {
    return this.userForms.get('colors') as FormArray
  }

  addUser() {
    const userGroup = this.fb.group({
      user: [],
      colors: this.fb.array([])
    })
    this.userForms.push(userGroup);
  }

  deleteUser(i) {
    this.userForms.removeAt(i);
  }

  addColor() {
    const colorGroup = this.fb.group({
      color: []
    })
    this.colorForms.push(colorGroup);
  }

  deleteColor(i) {
    this.colorForms.removeAt(i)
  }

}

And this is my html code:

<form [formGroup]="myForm">

  <hr>

  <input formControlName="email">

  <div formArrayName="users">

    <div *ngFor="let user of userForms.controls; let i=index" [formGroupName]="i">

      <input formControlName="user">

      <button (click)="deleteUser(i)">Delete User</button>

      <div formArrayName="colors">

        <div *ngFor="let color of colorForms.controls; let t=index" [formGroupName]="t">

          <input formControlName="color">

          <button (click)="deleteColor(t)">Delete Color</button>

        </div>

      </div>

    </div>

  </div>

  <button (click)="addUser()">Add User</button>

</form>

Obviously this doesn't work, so I am trying to comprehend what I need to do.


Solution

  • It doesn't work because you do not take into account the index in which controls are stored.

    For example

    get colorForms() {
        return this.userForms.get('colors') as FormArray
    }
    

    won't work since userForms returns FormArray and you have to specify index to get colors control which belongs to specific user.

    So it might look like:

    getColors(index) {
      return this.userForms.get([index, 'colors']) as FormArray;
    }
    

    and in html:

    <div *ngFor="let color of getColors(i).controls;...>
    

    Also you need to keep it in mind when working with colors array:

    addColor(index: number) {
      const colorGroup = this.fb.group({
        color: []
      })
    
      this.getColors(index).push(colorGroup);
            ^^^^^^^^^^^^
     use the them method to get correct FormArray
    }
    
    deleteColor(userIndex: number, colorIndex: number) {
      this.getColors(userIndex).removeAt(colorIndex);
    }
    

    See also Ng-run Example