javascriptangularangular-materialangular-datatablesangular-material-table

Set the MatCellDef iterator for a specific JSON such that the matsort and matsearch work in a matTable


So I have a JSON Of the following Format:

        {
            "loan": {
                "loanKey": 6575,
                "poolNum": "300801056",
                "triggers": [
                    {
                        "id": 4276,
                        "triggerType": "DSCR",
                        "triggerValue": 1.2
                    }
                ]
            }
        },
        {
            "loan": {
                "loanKey": 6651,
                "poolNum": "30308716"
            }
        },
        {
            "loan": {
                "loanKey": 8224,
                "poolNum": "998"
            }
        },
        {
            "loan": {
                "loanKey": 8225,
                "poolNum": "999"
            }
        },
        {
            "loan": {
                "loanKey": 9877,
                "poolNum": "998"
            }
        },
        {
            "loan": {
                "loanKey": 9878,
                "poolNum": "999"
            }
        }
]

Now I am accessing a certain row and its internal elements in a way below:

<ng-container matColumnDef="loanKey">
         <th mat-header-cell *matHeaderCellDef mat-sort-header>Loan Key/ID</th>
         <td mat-cell *matCellDef="let row">
         {{ row.loan.loanKey }}
         </td>
</ng-container>

With this I am getting the value I req. to display on the table, but due to mismatch of the matColumnDef name and interpolation name the matSearch and Sort arent working. Can anyone help fix this?


Solution

  • Solution 1: Without MatTableDataSource

    From MatTable Sorting section,

    If you are not using the MatTableDataSource, but instead implementing custom logic to sort your data, listen to the sort's (matSortChange) event and re-order your data according to the sort state. If you are providing a data array directly to the table, don't forget to call renderRows() on the table, since it will not automatically check the array for changes.

    1. Trigger sortChange($event) function when matSortChange event is fired.
    <table
      mat-table
      [dataSource]="data"
      class="mat-elevation-z8"
      matSort
      (matSortChange)="sortChange($event)"
    >
      <ng-container matColumnDef="loanKey">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Loan Key/ID</th>
        <td mat-cell *matCellDef="let row">
          {{ row.loan.loanKey }}
        </td>
      </ng-container>
    
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>
    
    1. Define a dictionary with the column name, the sorting direction, and its sorting logic.

    2. In sortChange function, sort the data by getting the sorting implementation by sort.active (column name) and sort.direction (sort direction) from sortDict. And also execute table.renderChanges() so that the MatTable will reflect the changes.

    @ViewChild(MatTable) table: MatTable<Model>;
    
    sortDict = {
      loanKey: {
        asc: (a: Model, b: Model) => a.loan.loanKey - b.loan.loanKey,
        desc: (a: Model, b: Model) => b.loan.loanKey - a.loan.loanKey,
      },
    };
    
    sortChange(sortState: Sort) {
      this.data.sort(this.sortDict[sortState.active][sortState.direction]);
    
      this.table.renderRows();
    }
    

    Demo Solution 1 @ StackBlitz


    Solution 2: With MatTableDataSource

    From MatTable Sorting section,

    Note that if the data properties do not match the column names, or if a more complex data property accessor is required, then a custom sortingDataAccessor function can be set to override the default data accessor on the MatTableDataSource.

    Credit to the answer from @Steve Sanders' on Angular Material 2 DataTable Sorting with nested objects.

    Concern from his answer is that you need to override the sortingDataAccessor and sort from the dataSource in AfterViewInit lifecycle.

    <table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort>
      <ng-container matColumnDef="loanKey">
        <th mat-header-cell *matHeaderCellDef mat-sort-header>Loan Key/ID</th>
        <td mat-cell *matCellDef="let row">
          {{ row.loan.loanKey }}
        </td>
      </ng-container>
    
      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
      <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
    </table>
    
    @ViewChild(MatSort) sort: MatSort;
    dataSource: MatTableDataSource<Model>;
    
    ngOnInit() {
      this.dataSource = new MatTableDataSource(this.data);
    }
    
    ngAfterViewInit() {
      this.dataSource.sortingDataAccessor = (item, property) => {
        switch (property) {
          case 'loanKey':
            return item.loan.loanKey;
          default:
            return item[property];
        }
      };
      this.dataSource.sort = this.sort;
    }
    

    Demo Solution 2 @ StackBlitz