Implementing Recursive Nested Tables with Dynamic Data in Angular Using DevExtreme Master-Detail Component

The problem involves implementing a dynamic and potentially infinitely recursive nested table structure in an Angular application using the DevExtreme Master-Detail component. The main challenges include:

Dynamic Data: The data structure for the tables is dynamic, meaning the columns and rows can differ every time the data is loaded. This requires a flexible approach to generating table structures and content.

Variable Nesting: Some rows may contain nested tables, while others do not. Furthermore, the depth of nesting can vary, with some rows containing multiple levels of nested tables.

I need to create a recursive Angular component or find a solution to be capable of rendering a table with nested tables based on the provided data, handling the dynamic creation of columns, and ensuring that the nested data is correctly displayed and managed using the Master-Detail component of DevExtreme.

here is my current implementation. but the problem with this solution is that I create a new component for each level of nesting and pass the data.

**Main Table : **

 import { Component} from '@angular/core';

@Component({
  selector: 'app-hierachy-table',
  template:`<dx-data-grid id="gridContainer" [dataSource]="employees" keyExpr="ID" [showBorders]="true" [allowColumnReordering]="true">
  <dxo-master-detail [enabled]="true" [template]="employees"></dxo-master-detail>
  <div *dxTemplate="let employee of employees">
      <employee-detail [key]="employee.key"></employee-detail>
  </div>
</dx-data-grid>`,
})
export class HierachyTableComponent
{
  employees: any = [{
    ID: 1,
    Prefix: 'Mr.',
    FirstName: 'John',
    LastName: 'Heart',
    Position: ['CEO', 'CTFO'],
    State: 'California',
    BirthDate: '1964/03/16',
  },
  {
    ID: 2,
    Prefix: 'Mrs.',
    FirstName: 'Olivia',
    LastName: 'Peyton',
    Position: 'Sales Assistant',
    State: 'California',
    BirthDate: '1981/06/03',
  },
  {
    ID: 3,
    Prefix: 'Mr.',
    FirstName: 'Robert',
    LastName: 'Reagan',
    Position: 'CMO',
    State: 'Arkansas',
    BirthDate: '1974/09/07',
  },
  {
    ID: 4,
    Prefix: 'Ms.',
    FirstName: 'Greta',
    LastName: 'Sims',
    Position: 'HR Manager',
    State: 'Georgia',
    BirthDate: '1977/11/22',
  },
  {
    ID: 5,
    Prefix: 'Mr.',
    FirstName: 'Brett',
    LastName: 'Wade',
    Position: 'IT Manager',
    State: 'Idaho',
    BirthDate: '1968/12/01',
  },
  {
    ID: 6,
    Prefix: 'Mrs.',
    FirstName: 'Sandra',
    LastName: 'Johnson',
    Position: 'Controller',
    State: 'Utah',
    BirthDate: '1974/11/15',
  },
  {
    ID: 7,
    Prefix: 'Mr.',
    FirstName: 'Kevin',
    LastName: 'Carter',
    Position: 'Shipping Manager',
    State: 'California',
    BirthDate: '1978/01/09',
  }];
  constructor() { }

}

First Level Nesting Component:

@Component({
  selector: 'employee-detail',
  template: `<dx-data-grid [dataSource]="tasksDataSource" [showBorders]="true" [columnAutoWidth]="true">
  <dxo-master-detail [enabled]="true" [template]="employeeDetails"></dxo-master-detail>
  <div *dxTemplate="let employee of employeeDetails">
      <employee-development [key]="employee.key"></employee-development>
  </div>
</dx-data-grid>`
})
export class DetailGridComponent implements AfterViewInit
{
  public tasksDataSource: any;

  @Input()
  public key: number | any;
  employeeDetails: any = [
    {
      EmployeeID: 1,
      EmploymentYear: '2012',
      JoinDate: '2012/04/10',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 120,
    },
    {
      EmployeeID: 2,
      EmploymentYear: '2012',
      JoinDate: '2014/05/20',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Inactive',
      ProjectsCompleted: 60,
    },
    {
      EmployeeID: 3,
      EmploymentYear: '2012',
      JoinDate: '2020/06/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'On Leave',
      ProjectsCompleted: 30,
    },
    {
      EmployeeID: 4,
      EmploymentYear: '2012',
      JoinDate: '2006/07/25',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 450,
    },
    {
      EmployeeID: 5,
      EmploymentYear: '2012',
      JoinDate: '2012/08/05',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 110,
    },
    {
      EmployeeID: 6,
      EmploymentYear: '2012',
      JoinDate: '2012/09/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 95,
    },
    {
      EmployeeID: 7,
      EmploymentYear: '2012',
      JoinDate: '2012/10/25',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 105,
    },
    {
      EmployeeID: 8,
      EmploymentYear: '2012',
      JoinDate: '2012/11/05',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 92,
    },
    {
      EmployeeID: 9,
      EmploymentYear: '2012',
      JoinDate: '2012/12/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 88,
    },
    {
      EmployeeID: 10,
      EmploymentYear: '2012',
      JoinDate: '2012/04/10',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 115,
    }
  ];

  ngAfterViewInit()
  {
    this.tasksDataSource = new DataSource({
      store: new ArrayStore({
        data: this.employeeDetails,
        key: 'EmployeeID',
      }),
      filter: ['EmployeeID', '=', this.key],
    });
  }

}

third Level Nesting:

@Component({
  selector: 'employee-development',
  template: `<dx-data-grid [dataSource]="dataSource" [showBorders]="true" [columnAutoWidth]="true">
  <dxo-master-detail [enabled]="true" [template]="employeeDevelopments"></dxo-master-detail>
  <div *dxTemplate="let development of employeeDevelopments">
      <employee-compansation [key]="development.key"></employee-compansation>
  </div>
</dx-data-grid>`})
export class EmployeeDevelopmentComponent
{
  @Input()
  key: any;
  employeeDevelopments: any = [
    {
      EmployeeID: 1,
      TrainingSessionsAttended: 5,
      SkillsAcquired: ['Project Management', 'Time Management', 'Advanced Excel'],
      LastPerformanceReviewScore: 9,
      NextReviewDate: '2024/04/10',
    },
    {
      EmployeeID: 2,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Communication', 'Critical Thinking'],
      LastPerformanceReviewScore: 7,
      NextReviewDate: '2024/05/20',
    },
    {
      EmployeeID: 3,
      TrainingSessionsAttended: 4,
      SkillsAcquired: ['Team Leadership', 'Conflict Resolution', 'Public Speaking'],
      LastPerformanceReviewScore: 8,
      NextReviewDate: '2024/06/15',
    },
    {
      EmployeeID: 4,
      TrainingSessionsAttended: 6,
      SkillsAcquired: ['Strategic Planning', 'Budget Management', 'Negotiation'],
      LastPerformanceReviewScore: 9.5,
      NextReviewDate: '2024/07/25',
    },
    {
      EmployeeID: 5,
      TrainingSessionsAttended: 2,
      SkillsAcquired: ['Data Analysis', 'Critical Thinking'],
      LastPerformanceReviewScore: 8.5,
      NextReviewDate: '2024/08/05',
    },
    {
      EmployeeID: 6,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Creative Problem Solving', 'Effective Communication'],
      LastPerformanceReviewScore: 7.5,
      NextReviewDate: '2024/09/15',
    },
    {
      EmployeeID: 7,
      TrainingSessionsAttended: 4,
      SkillsAcquired: ['Project Management', 'Team Motivation', 'Agile Methodologies'],
      LastPerformanceReviewScore: 8,
      NextReviewDate: '2024/10/25',
    },
    {
      EmployeeID: 8,
      TrainingSessionsAttended: 2,
      SkillsAcquired: ['Time Management', 'Stress Management'],
      LastPerformanceReviewScore: 7,
      NextReviewDate: '2024/11/05',
    },
    {
      EmployeeID: 9,
      TrainingSessionsAttended: 5,
      SkillsAcquired: ['Digital Marketing', 'SEO Basics', 'Content Creation'],
      LastPerformanceReviewScore: 8.5,
      NextReviewDate: '2024/12/15',
    },
    {
      EmployeeID: 10,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Analytical Thinking', 'Data Presentation'],
      LastPerformanceReviewScore: 9,
      NextReviewDate: '2024/04/10',
    }
  ];

  dataSource: any;
  ngAfterViewInit()
  {
    this.dataSource = new DataSource({
      store: new ArrayStore({
        data: this.employeeDevelopments,
        key: 'EmployeeID',
      }),
      filter: ['EmployeeID', '=', this.key],
    });

  }
}

I am expecting to have a component where it renders the nesting automatically. instead of creating new component for each level of nesting.