0

I have 2 tables that I need to display in the following manner: Nested Table I'm populating 2 different arrays

storeTableData: any=[];
employeeTableData: any=[];

The storeTableData has the following fields: StoreID, Name, Address
The employeeTableData has the following fields: StoredID, Name, Role

I'm not sure if I have to turn my data into an array like this first (and if so, how?):

Store[] = [
{
  StoreID: "1",
  Store: "Staples",
  address: "123 Main Street, San Diego CA 12345",
  employees: [
    {
      StoreID: "1",
      Name: "John Doe",
      Role: "Manager"
    },
    {
      StoreID: "1",
      Name: "John Smith",
      Role: "Cashier"
    },
    {
      StoreID: "1",
      Name: "Jane Doe",
      Role: "Shipping"
    },
},
{
  StoreID: "2",
  Store: "Best Buy",
  address: "456 Main Street, San Diego CA 12345",
  employees: [
    {
      StoreID: "2",
      Name: "John Smith",
      Role: "Manager"
    },
    {
      StoreID: "2",
      Name: "Jane Doe",
      Role: "Cashier"
    },
    {
      StoreID: "2",
      Name: "John Doe",
      Role: "Shipping"
    },
},
]

Or is there a way I can just do it via a nested ngif in the html or is there some other way?

2
  • I don't think that splitting your current Store array, which contains both store info and employees array into two separate arrays a good choice. Assume that when generating the employee table in the employees column (based on your screenshot) will need additional effort to find the employees that fall under which store. Commented Jul 11 at 0:10
  • try this: stackblitz.com/edit/… you have tagged angular-material but there is no mention of that in the question. If using angular material use @YongShun answer Commented Jul 11 at 4:40

1 Answer 1

2

As mentioned in the comment, there is no point to split the store and employees data into separate array if you are constructing the nested table as the screenshot.

Updated

Well, if both stores and employees data come from separate arrays, you should join it together in your back-end API/database so you will need single API call. (Unfortunately you didn't cover on how you obtain these data from the back-end API or database, hence I will not cover this topic).

If you are not able/granted to build/change the API for the mentioned joining data, you can look for joining both storeTableData and employeeTableData in the front-end:

const storeWithEmployeesData: any[] = storeTableData.map((store) => ({
    ...store,
    employees: employeeTableData.filter((e) => store.StoreID == e.StoreID)
}));

Taking the example for my written answer on Expanding multiple rows in a nested mat table in Angular not working, you can construct the nested table in the column as well.

Note that if you implement the sorting feature in the nested table, your employees array should be transformed to MatTableDataSource<T> type and assign the MatSort instance accordingly.

<table
  mat-table
  #outerSort="matSort"
  [dataSource]="dataSource"
  multiTemplateDataRows
  class="mat-elevation-z8"
  matSort
>
  <ng-container matColumnDef="Store">
    <th mat-header-cell *matHeaderCellDef mat-sort-header=>Store</th>
    <td mat-cell *matCellDef="let element">{{element.Store}}</td>
  </ng-container>

  <ng-container matColumnDef="address">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Address</th>
    <td mat-cell *matCellDef="let element">{{element.address}}</td>
  </ng-container>

  <ng-container matColumnDef="employees">
    <th mat-header-cell *matHeaderCellDef>Employees</th>
    <td mat-cell *matCellDef="let element">
      <table
        #innerTables
        mat-table
        #innerSort="matSort"
        [dataSource]="element.employees"
        matSort
      >
        <ng-container
          *ngFor="let innerColumn of innerDisplayedColumns"
          [matColumnDef]="innerColumn"
        >
          <th mat-header-cell *matHeaderCellDef [mat-sort-header]="innerColumn">
            {{innerColumn}}
          </th>
          <td mat-cell *matCellDef="let element">{{element[innerColumn]}}</td>
        </ng-container>
        <tr mat-header-row *matHeaderRowDef="innerDisplayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: innerDisplayedColumns;"></tr>
      </table>
    </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="columnsToDisplay"></tr>
  <tr mat-row *matRowDef="let element; columns: columnsToDisplay;"></tr>
</table>
import {
  QueryList,
  ViewChild,
  ViewChildren,
} from '@angular/core';
import {
  MatTable,
  MatTableDataSource,
  MatTableModule,
} from '@angular/material/table';
import { MatSort, MatSortModule } from '@angular/material/sort';

@ViewChild('outerSort', { static: true }) sort: MatSort;
  dataSource: MatTableDataSource<any>;
@ViewChildren('innerSort') innerSort: QueryList<MatSort>;
@ViewChildren('innerTables') innerTables: QueryList<MatTable<any>>;
columnsToDisplay = ['Store', 'address', 'employees'];
innerDisplayedColumns = ['Name', 'Role'];

ngOnInit() {
  let storesData: any[] = this.stores.map((store) => ({
    ...store,
    employees: new MatTableDataSource(store.employees || []),
  }));

  this.dataSource = new MatTableDataSource(storesData);
  this.dataSource.sort = this.sort;
}

ngAfterViewInit() {
  this.innerTables.forEach((table, index) => {
    const dataSource = table.dataSource as MatTableDataSource<any>;
    dataSource.sort = this.innerSort.toArray()[index];
  });
}

Demo @ StackBlitz

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.