Search code examples
jsonangulartypescriptangular-materialmat-table

Correctly display in a mat-table a data list grouped with dynamic columns


I have a problem that unfortunately I cannot resolve.

My API returns me a list of data like this:

[
  {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"},
  {grade: "Grade B", id: 2, ifsGrade: "B1XX", ifsType: "02XX", points: 15, type: "Type_1"},
  {grade: "Grade C", id: 3, ifsGrade: "C1XX", ifsType: "03XX", points: 1,  type: "Type_1"},
  {grade: "Grade A", id: 4, ifsGrade: "A2XX", ifsType: "04XX", points: 23, type: "Type_2"},
  {grade: "Grade B", id: 5, ifsGrade: "B2XX", ifsType: "05XX", points: 26, type: "Type_2"}
]

And I group my data by type like this:

Array.prototype.groupBy = function(k) {
   return this.reduce((acc, item) => (acc[item[k]] = [...(acc[item[k]] || []), item], acc), {});
};

var TABLE_DATA = Object.values(API_DATA.groupBy("type"));

[
  [
    {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"},
    {grade: "Grade B", id: 2, ifsGrade: "B1XX", ifsType: "02XX", points: 15, type: "Type_1"},
    {grade: "Grade C", id: 3, ifsGrade: "C1XX", ifsType: "03XX", points: 1,  type: "Type_1"}
  ],
  [
    {grade: "Grade A", id: 4, ifsGrade: "A2XX", ifsType: "04XX", points: 23, type: "Type_2"},
    {grade: "Grade B", id: 5, ifsGrade: "B2XX", ifsType: "05XX", points: 26, type: "Type_2"}
  ]
]

I would like to be able to display the data in a Angular Material mat-table like this: enter image description here

So I do have a list for my dataSource but the elements of this list are lists too. So I don't know how to display the items correctly.

So for the table columns you would need something dynamic like this, but I don't know how:

<ng-container matColumnDef="{{column}}" *ngFor="let column of definedColumns">
  <th mat-header-cell *matHeaderCellDef> {{column}} </th>
  <td mat-cell *matCellDef="let element"> {{element[column]}} </td>
</ng-container>

Stackblitz link here

Thanks for your help


Solution

  • Data initialization with grouping by type :

    ngOnInit() {
        this.API_DATA = [
          {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"},
          {grade: "Grade B", id: 2, ifsGrade: "B1XX", ifsType: "02XX", points: 15, type: "Type_1"},
          {grade: "Grade C", id: 3, ifsGrade: "C1XX", ifsType: "03XX", points: 1,  type: "Type_1"},
          {grade: "Grade A", id: 4, ifsGrade: "A2XX", ifsType: "04XX", points: 23, type: "Type_2"},
          {grade: "Grade B", id: 5, ifsGrade: "B2XX", ifsType: "05XX", points: 16, type: "Type_2"}
        ];
    
        const key = 'type'
        const dataTable = Object.values(this.API_DATA.reduce((acc, item) => (acc[item[key]] = [...(acc[item[key]] || []), item], acc), {}));
    
        this.dataSource = dataTable;
      }
    

    mat-table Angular material with dynamic columns :

    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
      
      <ng-container matColumnDef="{{column}}" *ngFor="let column of definedColumns; let index = index;">
        <th mat-header-cell *matHeaderCellDef> {{column}} </th>
        <td mat-cell *matCellDef="let elements">
          <span *ngFor="let element of elements; let first = first;">
            <span *ngIf="index == 0 && first">{{element.type}}</span>
            <span *ngIf="element.grade == column">{{element.points}}</span>
          </span>
        </td>
      </ng-container>
    
      <ng-container matColumnDef="action">
        <th mat-header-cell *matHeaderCellDef class="text-right">
            <button mat-icon-button (click)="log('add')">
                <mat-icon color="accent">add_circle_outline</mat-icon>
            </button>
        </th>
        <td mat-cell *matCellDef="let gradingIndex" class="text-right">
            <button mat-icon-button>
                <mat-icon color="primary" (click)="log(gradingIndex)">edit</mat-icon>
            </button>
            <button mat-icon-button (click)="log(gradingIndex)">
                <mat-icon color="warn">delete</mat-icon>
            </button>
        </td>
      </ng-container>
    
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
    </table>
    

    Result :

    enter image description here

    Stackblitz : here