Search code examples
angularformarrayformgroups

Create Form Groups Based on Number of Records


I want to create FormGroups/FormArrays inside mat-expansion-panel based on number of records I have in my database.

I have a group table
id | desc
1 | Group 1
2 | Group 2

I have detail table
id | desc | group_id
1 | Detail 1.1 | 1
2 | Detail 1.2 | 1
3 | Detail 2.1 | 2

Now, I have this codes and it will loop based on number of records

HTML

 <mat-expansion-panel *ngFor="let group of groups"> <!-- based on group table  -->
   <form [formGroup]="group.formGroup">
     <div formArrayName="group.formArray" > 
       <div *ngFor="let detail of details"> <!-- based on detail table -->
       </div>
     </div>
   </form>
 </mat-expansion-panel>

TS

 ????????

I know how to create form group and form array but doing it based on number of records that is divided by mat-expansion-panel... There's where my problem.


Solution

  • Really I don't know what king of formGroup/formArray you want to create. I suppose you want a formArray of formGroup, each formGroup has as formControl "id","desc" and a formArray "detail". So we can get something like

    [
      {
        "id": 1,
        "desc": "group1",
        "detail": [
          {
            "id": 1,
            "desc": "Detail 1.1."
          },
          {
            "id": 2,
            "desc": "Detail 1.2."
          }
        ]
      },
      {
        "id": 2,
        "desc": "group2",
        "detail": [
          {
            "id": 3,
            "desc": "Detail 2.1."
          }
        ]
      }
    ]
    

    to manage FormArrays of FormGroup, it's usefull create function that return a formGroup, like

    createGroup(data:any):FormGroup
      {
        data=data || {id:null,desc:null,detail:null}
        return new FormGroup({
          id:new FormControl(data.id),
          desc:new FormControl(data.desc),
          detail:data.detail && data.detail.length?
                 new FormArray(data.detail.map(detail=>this.createDetail(detail))):
                 new FormArray([])
        })
      }
      createDetail(data:any):FormGroup
      {
        data=data || {id:null,desc:null}
        return new FormGroup({
          id:new FormControl(data.id),
          desc:new FormControl(data.desc),
        })
      }
    

    Well if we has some like

      group=[{id:1,desc:"group1"},{id:2,desc:"group2"}]
      detail=[{id:1,desc:"Detail 1.1.",group_id:1},
      {id:2,desc:"Detail 1.2.",group_id:1},
      {id:3,desc:"Detail 2.1.",group_id:2}
      ]
    

    We can create a formArray in ngOnInit

      this.form=new FormArray(this.group.map((x:any)=>{
        return this.createGroup({
          ...x,
          detail:this.detail.filter(d=>d.group_id==x.id)
        })
       }))
    

    See how we create a FormArray transform each element of this.group to a FormGroup. we pass to our function as "data" the value of each "group" and a new protertie, "detail" that it's an array, the values of details which groupId are equals to id

    Well it's the difficult part. the funny is place all in an accordeon

    <form *ngIf="form" [formGroup]="form">
    <mat-accordion>
      <mat-expansion-panel *ngFor="let grp of form.controls" [formGroup]="grp">
        <mat-expansion-panel-header>
          <mat-panel-title>
                <mat-form-field>
                <input matInput formControlName="id">
        </mat-form-field>
        <mat-form-field>
                <input matInput formControlName="desc">
        </mat-form-field>
          </mat-panel-title>
        </mat-expansion-panel-header>
        <div formArrayName="detail">
          <div *ngFor="let detail of grp.get('detail').controls;let i=index" [formGroupName]="i">
            <mat-form-field>
                <input matInput formControlName="id">
            </mat-form-field>
            <mat-form-field>
                  <input matInput formControlName="desc">
            </mat-form-field>
          </div>
        </div>
      </mat-expansion-panel>
    </mat-accordion>
    </form>
    

    see that we are using the way *ngFor="let grp of form.controls" [formGroup]="grp" to get the group of the formArray. This is the manner to manage a FormArray, put <form [formGroup]="myFormArray"> and loop over myFormArray.controls

    You can see in stackblitz

    NOTE: I put as editable and part of the form the ids too, when really I think it's not necesary

    NOTE2: in a submit, you need create a new table of details mapping the form.value