Search code examples
angulartypescriptsortingangular-materialangular-material-table

Angular Material - How to sort the data for mat-table in the loop


I am displaying the mat -table inside a loop. The records are displaying correctly and sorting arrows are also displayed. I've used MatSort but it is not working. Can anyone help me out where I am getting wrong?

Here is my code.

HTML:

<div *ngFor="let rc of filteredSizeCode; let i = index" class="table-responsive table-content">
  <h3 class="text-primary mt-1 mb-3">Unit Type : {{rc.unitTypeSizeConcat[0]}}</h3>
  <table mat-table [dataSource]="datasource[i]" matSort class="mat-elevation-z8" matSortDisableClear>
    <ng-container matColumnDef="unitRateType">
      <th class="centered-sorted-header" mat-header-cell *matHeaderCellDef mat-sort-header>Unit Rate Filter</th>
      <td mat-cell *matCellDef="let row; let i=index">{{row.list.unitRateType}}</td>
    </ng-container>
    <ng-container matColumnDef="rateChangeType">
      <th class="centered-sorted-header" mat-header-cell *matHeaderCellDef mat-sort-header>Rate Change Type</th>
      <td mat-cell *matCellDef="let row; let i=index">{{row.list.rateChangeType}}</td>
    </ng-container>
    <ng-container matColumnDef="effectiveDate">
      <th class="centered-sorted-header" mat-header-cell *matHeaderCellDef mat-sort-header>Effective Date</th>
      <td mat-cell *matCellDef="let row; let i=index">{{row.list.effectiveDate | date: 'MM-dd-yyyy'}}</td>
    </ng-container>
    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
</div>

TS:

displayedColumns = [
  'unitRateType',
  'rateChangeType',
  'effectiveDate'
];
@ViewChild(MatSort) sort: MatSort;
datasource: MatTableDataSource<any[]>[] = [];

ngOnInit(){
  this.filteredSizeCode.forEach((size: any) => {
    let data: any[] = size.list.map((x: any) => ({ ...size, list: x }));
    console.log(data);
    this.datasource.push(new MatTableDataSource(data));
  });

  this.datasource[0].sortingDataAccessor = (item: any, property) => {
    console.log(item);
    switch (property) {
      case 'noticeDays':
        return item.list.noticeDays;
      case 'unitRateType':
        return item.list.unitRateType;
      case 'effectiveDate':
        return item.list.effectiveDate;
      default: return item[property];
    }
  }
  
  this.datasource[0].sort = this.sort;
}

Solution

  • Assume that you have imported MatSortModule.

    You are getting the MatSort directive from the view, you are only able to get it in the AfterViewInit lifecycle.

    Thus, move the logic for your custom sortingDataAccessor and initialize MatSort instance in MatTableDataSource in the ngAfterViewInit method.

    ngAfterViewInit() {
      this.datasource[0].sortingDataAccessor = (item: any, property) => {
        console.log(item);
        switch (property) {
          case 'noticeDays':
            return item.list.noticeDays;
          case 'unitRateType':
            return item.list.unitRateType;
          case 'effectiveDate':
            return item.list.effectiveDate;
          default:
            return item[property];
        }
      };
    
      this.datasource[0].sort = this.sort;  
    }
    

    For multiple MatTableDataSource instances, you need to get all the MatSort directives via @ViewChildren and initialize the MatSort instance by index.

    import { QueryList, ViewChildren } from '@angular/core';
    @ViewChildren(MatSort) sorts: QueryList<MatSort>;
    
    ngAfterViewInit() {
      for (let i = 0; i < this.datasource.length; i++) {
        this.datasource[i].sortingDataAccessor = (item: any, property) => {
          console.log(item);
          switch (property) {
            case 'noticeDays':
              return item.list.noticeDays;
            case 'unitRateType':
              return item.list.unitRateType;
            case 'effectiveDate':
              return item.list.effectiveDate;
            default:
              return item[property];
          }
        };
    
        this.datasource[i].sort = this.sorts.get(i)!;
      }
    }
    

    Demo @ StackBlitz