Search code examples
angularangular-materialangular-material-table

Angular 15 : How to make expandable rows group with Mat-Table and/or Mat-Expansion Panel


I'm working on a frontend Angular project in which i have to display datasets rows in a Mat-Table, those rows are grouped by status and that have to be expandable to show the dataset linked to that status.

Here's an example of the data i have to display :

data: [
        {
          status: "Processing",
          name: "test",
          id: 1,
          type: "POI",
          last_update: "yesterday",
          update_frequency: "everydays",
          source: "DATATOURISME"
        },
        {
          status: "Processing",
          name: "another dataset",
          id: 2,
          type: "POI",
          last_update: "yesterday",
          update_frequency: "everydays",
          source: "DATATOURISME"
        },
        {
          status: "Blocked",
          name: "dataset 3",
          id: 3,
          type: "POI",
          last_update: "yesterday",
          update_frequency: "everydays",
          source: "DATATOURISME"
        },
        {
          status: "Blocked",
          name: "4rth one",
          id: 4,
          type: "POI",
          last_update: "yesterday",
          update_frequency: "everydays",
          source: "DATATOURISME"
        },
        {
          status: "Suspended",
          name: "here's the fifth one",
          id: 5,
          type: "POI",
          last_update: "yesterday",
          update_frequency: "everydays",
          source: "DATATOURISME"
        }
      ]

I managed to separate the rows by status but i can't find out how to make the status expandable to show all their related datasets. Let me show you what i've done so far.

datasets-table.component.html :

<div class="table-container">
    <table mat-table [dataSource]="datasetsList" class="mat-elevation-z8">

        <ng-container matColumnDef="name">
            <th mat-header-cell *matHeaderCellDef>
                <span class="column-name">{{ 'DATASET_LIST.COLUMNS.NAME' | translate }}</span>
            </th>
    
            <td mat-cell *matCellDef="let element"> {{element.name}} </td>
        </ng-container>
    
        ...
    
        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="isLoading"></tr>

        <!-- Group header -->
        <ng-container matColumnDef="groupHeader">
            <td colspan="999" mat-cell *matCellDef="let groupBy; let element">{{element.group}}</td>
        </ng-container>

        <tr mat-row *matRowDef="let row; columns: ['groupHeader']; when: isGroup"> </tr>
    </table>
  
</div>  

The processed data once i applied some code on it :

0: Object { group: "Processing", isGroupBy: true }
​
1: Object { status: "Processing", name: "test", id: 1, … }
​
2: Object { status: "Processing", name: "another dataset", id: 2, … }
​
3: Object { group: "Blocked", isGroupBy: true }
​
4: Object { status: "Blocked", name: "dataset 3", id: 3, … }
​
5: Object { status: "Blocked", name: "4rth one", id: 4, … }
​
6: Object { group: "Suspended", isGroupBy: true }
​
7: Object { status: "Suspended", name: "here's the fifth one", id: 5, … }

The result looks like this :given table But i would like it to looks like this (with the expandable status rows) : Expected table

Here's a live example : https://stackblitz.com/edit/angular-ivy-hm867d?file=src/app/datasets-table.component.html


Solution

  •             <mat-table [dataSource]="dataSource" class="mat-elevation-z8">
                  <ng-container *ngFor="let column of columns; let i = index" matColumnDef="{{ column.field }}">
                    <mat-header-cell *matHeaderCellDef>{{ column.field }}
                      <button class="grid-view-header-menu"
                        mat-icon-button [matMenuTriggerFor]="menu" >
                        <mat-icon >menu</mat-icon>
                      </button>
                      <mat-menu #menu>
                        <button mat-menu-item (click)="groupBy($event, column);">Group By This Field</button>
                        <button mat-menu-item (click)="unGroupBy($event, column);" >Ungroup</button>
                      </mat-menu>
                    </mat-header-cell>
                    <mat-cell *matCellDef="let row">{{ row[column.field] }}</mat-cell>
                  </ng-container>
                    <mat-header-row mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
                    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
                    <!-- Group header -->
                    <ng-container matColumnDef="groupHeader">
                        <mat-cell colspan="999" *matCellDef="let group">
                      <mat-icon *ngIf="group.expanded">expand_less</mat-icon>
                      <mat-icon *ngIf="!group.expanded">expand_more</mat-icon>
                            <strong>{{groupByColumns[group.level-1]}} = {{group[groupByColumns[group.level-1]]}} ({{group.totalCounts}})</strong>
                      </mat-cell>
                  </ng-container>
                  <mat-row *matRowDef="let row; columns: ['groupHeader']; when: isGroup" (click)="groupHeaderClick(row)"> </mat-row>
                </mat-table>
    

    Here you can find a complete example what you need. https://stackblitz.com/edit/angular-material-table-row-grouping?file=src%2Fapp%2Fapp.component.html