DEV Community

Cover image for How to Manage Parent-Child Data Communication and Update Nested Data Structures Dynamically in Angular
Md. Injamul Alam
Md. Injamul Alam

Posted on

How to Manage Parent-Child Data Communication and Update Nested Data Structures Dynamically in Angular

Although this post might be lengthy, my problem may not seem like a problem to someone more experienced than me. Still, I am writing it in the hope that it might help some of you.
I have divided this blog post into two parts. In the first part, I will discuss the coding problem I faced, making it easier for you to understand the solution. In the second part, I will explain how I solved my coding problem.
Firstly, the tech stack for the office project includes: Angular v17, .NET Core, Microsoft SQL, and the theme: PrimeNG v17.

The problem I’m discussing is related to retrieving saved data to update and set it in the entry form. In this case, part of the data needs to be looped through and set to the interface. Although the data was being set correctly, it was not displaying in the dropdown. Moreover, the data was not being modified correctly; even if it was, only the last element of the array was being updated.
I am not sharing the full code here due to privacy reasons. Instead, I have presented the main data as sample data to make it easier for you to understand.
JSON Data

{
  "masterId": 1,
  "masterDetails": [
      {
          "masterId": 1,
          "detailsData":[
          { 
            "detailsDataId": 123,
            "subDetailsData":[
             {
              "subDetailsDataId": 133,
              // here are other properties
             },
             {
              "subDetailsDataId": 134,
              // here are other properties
             }
           ]
          }
         // here are other elements of detailsData table
        ]
      }
  ]
}
Enter fullscreen mode Exit fullscreen mode

We know that in Angular, there are primarily two types of forms for managing and handling user input:

  1. Template-Driven Forms
  2. Reactive Forms

In the project, we are using a Template-Driven Form.
Here is the code:

<div *ngFor="let d of detailsData; let i = index">
  <p-button [label]="d.detailsDataId" icon="pi pi-plus" (click)="add(i)" />
  <div *ngFor="let subDetailD of detailsData.subDetailsData; let c = index">
    <div class="fromgrid grid my-1 align-items-center">
      <div class="p-fluid col-12 md:col-6 xl:col-5 md:px-1 py-1">
        <p-dropdown
          styleClass="w-full"
          class="w-full"
          [options]="dropDownData[detailsData.detailsDataId]"
          name="DetailsDataId"
          (ngModel)]="subDetailD.subDetailsDataId"
          optionValue="DetailsDataId"
          optionLabel="DetailsDataName"
          [filter]="true"
          filterBy="DetailsDataName"
          [showClear]="true"
          placeholder="Select"
        >
          <ng-template pTemplate="selectedItem" let-selectedOption>
            <div class="flex align-items-center gap-2">
              <div>{{ selectedOption?.DetailsDataName }}</div>
            </div>
          </ng-template>
          <ng-template let-data pTemplate="item">
            <div class="flex align-items-center gap-2">
              <div>{{ data.DetailsDataName }}</div>
            </div>
          </ng-template>
        </p-dropdown>
      </div>
      <div class="p-fluid col-6 md:col-3 xl:col-1 md:px-1 py-1">
        <p-button
          icon="pi pi-times"
          severity="danger"
          [outlined]="true"
          size="small"
          (click)="remove(i, c)"
        />
      </div>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

I used ngModel to set the data in the form. In this case, while looping through the data to set it in the interface, I encountered the issue. Despite making many changes to the code, no solution was found. Finally, I isolated the part of the code that was causing the problem as a child component.
Now, I am discussing the solution:

Parent Component (.html)

<div *ngFor="let d of detailsData; let i = index;">
<p-button [label]="d.detailsDataId" icon="pi pi-plus" (click)="add(i)" />
// Here is the child component which is looped based on the parent component
 <app-child-component [data]="d" [parentIndex]="I" 
  (remove)="remove(i, $event)" />  
</div>
Enter fullscreen mode Exit fullscreen mode

Parent Component (.ts)

add(index: number): void { // Add a new child component dynamically
    const newsubDetailD = { 
    name: `New Sub-Details 
    ${this.detailsData[index].subDetailsData.length + 1}` };
    this.detailsData[index].subDetailsData.push(newsubDetailD);
}

remove (index: number, childIndex: number): void { 
// Method to remove a specific child component (e.g., by index)
    this.detailsData[index].subDetailsData.splice(childIndex, 1);
}
Enter fullscreen mode Exit fullscreen mode

Child Component (.html)

<div *ngIf="data">
    <div *ngFor="let subDetailD of data.subDetailsData; let c = index">
        <div class="fromgrid grid my-1 align-items-center">
            <div class="p-fluid col-12 md:col-6 xl:col-5 md:px-1 py-1">
                <p-dropdown
                styleClass="w-full"
                class="w-full"
                [options]="dropDownData[data.detailsDataId]"
                name="DetailsDataId"
                (ngModel)]="subDetailD.subDetailsDataId"
                optionValue="DetailsDataId"
                optionLabel="DetailsDataName"
                [filter]="true"
                filterBy="DetailsDataName"
                [showClear]="true"
                placeholder="Select"
                >
                <ng-template pTemplate="selectedItem" let-selectedOption>
                    <div class="flex align-items-center gap-2">
                    <div>{{ selectedOption?.DetailsDataName }}</div>
                    </div>
                </ng-template>
                <ng-template let-data pTemplate="item">
                    <div class="flex align-items-center gap-2">
                    <div>{{ data.DetailsDataName }}</div>
                    </div>
                </ng-template>
                </p-dropdown>
            </div>
            <div class="p-fluid col-6 md:col-3 xl:col-1 md:px-1 py-1">
                <p-button
                icon="pi pi-times"
                severity="danger"
                [outlined]="true"
                size="small"
                (click)="remove(i, c)"
                />
            </div>
            <div class="p-fluid col-12 md:col-6 xl:col-1 md:px-1 py-1">
                <p-button icon="pi pi-times" (click)="remove(c)" severity="danger" [outlined]="true" size="small" pTooltip="Remove Counter" tooltipPosition="bottom" />
            </div>
        </div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

_Child Component (.ts) _

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
export class childComponent implements OnInit {
  constructor() {}
  ngOnInit() {} 
  @Input() data: any; // Accept data from parent
  @Input() parentIndex: number; // The index from the parent
  @Output() remove: EventEmitter<number> = new EventEmitter<number>(); 
  // Typed as number
  remove(childIndex: number): void {
   this.remove.emit(childIndex); // Emit the childIndex to the parent
  }

}
Enter fullscreen mode Exit fullscreen mode

By creating a child component, I successfully resolved the binding and looping issues that were previously causing trouble with ngModel. Sometimes, the solution lies in breaking things down into smaller, more manageable pieces. While this might seem like a simple fix to many, it was a valuable learning experience for me, and I hope it inspires others to approach similar challenges with curiosity and creativity. I'd love to hear your thoughts and insights—feel free to share them!

Top comments (0)