How to correctly use formGroup with nested data in angular reactive forms

2k Views Asked by At

I am new to angular and reactive forms.

I have a table with some form elements and I am trying to use reactive forms to save the data. I was able to loop and render nested behavior of the json but unable to bind properly the form array and its controls.

The formControlName is incorrectly looping and being wrongly applied to checkbox and input text.

app.component.ts

this.userData.forEach((item)=>{
  item.info.forEach((info)=> {
    const temp = new FormGroup({
      'active': new FormControl(info.active),
      'remarks': new FormControl(info.remarks),
      'id': new FormControl(item.id),
      'name': new FormControl(info.name),
      'number': new FormControl(info.number),
      'group': new FormControl(item.group)
    });

    (<FormArray>this.userForm.controls['userDetails']).push(temp);
  });
});

app.component.html

<div>
<h3>My Form</h3> 
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
    <table class="table">
        <thead>
            
        </thead>
        <tbody formArrayName="userDetails">
            <ng-container *ngFor="let item of userData; let i = index" formGroupName="{{ i }}">
                <tr>
                    <td colspan="5">
                        {{item.group}}
                    </td>
                </tr>
                <tr *ngFor="let info of item.info">
                    <td>
                        <input type="checkbox" formControlName="active">
                    </td>
                    <td>
                        {{info.id}}
                    </td>
                    <td>
                        {{info.name}}
                    </td>
                    <td>
                        {{info.number}}
                    </td>
                    <td>
                        <input type="text" formControlName="remarks">
                    </td>
                </tr>
            </ng-container>
        </tbody>
    </table>
    <div>
        <button class="btn btn-primary" type="submit">Save</button>
    </div>
</form>
</div>

data

userData = [
{
  "id": 123,
  "group": "A Group",
  "info": [
    {
      'name': "John Smith",
      'number': "789345612",
      'remarks': "Attended Last Session",
      'active': true
    },
    {
      'name': "Bob Mathers",
      'number': "987345120",
      'remarks': "",
      'active': false
    },
    {
      'name': "Steve Kieth",
      'number': "707549120",
      'remarks': "",
      'active': false
    }
  ]
},
{
  "id": 456,
  "group": "B Group",
  "info": [
    {
      'name': "Mia Anne P",
      'number': "880345009",
      'remarks': "",
      'active': false
    },
    {
      'name': "Mathew C Brady",
      'number': "7086183092",
      'remarks': "No Show",
      'active': false
    }
  ]
},
{
  "id": 789,
  "group": "C Group",
  "info": [
    {
      'name': "Stanley Jones",
      'number': "961096478",
      'remarks': "",
      'active': false
    },
    {
      'name': "Gina Qazyt",
      'number': "767654730",
      'remarks': "Arrived Late",
      'active': false
    }
  ]
}

]

As you can see in the below screenshot, the json data active and remarks field don't match with that of the form

enter image description here

I've tried placing

formGroupName="{{ i }}" and i = index 

in the second loop, but then the formControlName="active" applies to every first row of the group

1

There are 1 best solutions below

0
On

Do not know whether this is actually your requirement. Based on your output, I arranged the following FormGroup. This is how I would do this. Would like to see other's answers as well.

app.component.ts

userForm: FormGroup = new FormGroup({ userGroups: new FormArray([]) });

---

this.userData.forEach(
  (item) => {

    const userGroup = new FormGroup({
      id: new FormControl(item.id),
      group: new FormControl(item.group),
      userDetails: new FormArray([])
    });

    item.info.forEach((info) => {
      const userDetail = new FormGroup({
        name: new FormControl(info.name),
        number: new FormControl(info.number),
        remarks: new FormControl(info.remarks),
        active: new FormControl(info.active)
      });
      (userGroup.controls.userDetails as FormArray).push(userDetail);
    });

    (this.userForm.controls.userGroups as FormArray).push(userGroup);
  }
);

app.component.html

<div>
  <h3>My Form</h3>
  <form [formGroup]="userForm" (ngSubmit)="onSubmit(userForm.value)">
    <table class="table">
      <thead>

      </thead>
      <tbody formArrayName="userGroups">
        <ng-container *ngFor="let item of userData; let i = index" formGroupName="{{ i }}">
          <tr>
            <td colspan="5">
              {{item.group}}
            </td>
          </tr>
          <div formArrayName="userDetails">
            <tr *ngFor="let info of item.info; let j = index" formGroupName="{{ j }}">
              <td>
                <input type="checkbox" formControlName="active">
              </td>
              <td>
                {{info.id}}
              </td>
              <td>
                {{info.name}}
              </td>
              <td>
                {{info.number}}
              </td>
              <td>
                <input type="text" formControlName="remarks">
              </td>
            </tr>
          </div>
        </ng-container>
      </tbody>
    </table>
    <div>
      <button class="btn btn-primary" type="submit">Save</button>
    </div>
  </form>
</div>