Default sorting in Angular Material with nested objects

Viewed 4406

Im using mat table with sorting, where I want to set default sorting on a certain column.

This works for plain attributes but not for nested ones.

Here is my table

<table mat-table [dataSource]="dataSource" multiTemplateDataRows 
       matSort matSortActive="break" matSortDirection="asc"
       class="mat-elevation-z4 w-100">

My nested sorting

this.dataSource.sort = this.sort;
   this.dataSource.sortingDataAccessor = (item, property) => {
   switch(property) {
      case 'break': return item.break.start;
      default: return item[property];
   }
};

Also this nested sorting is fine, when you are manually clicking on the header to sort, it does what I expect but on default it doesn't sort, only shows the sorting arrow.

That's how it looks like on page load:

Here is the corresponding stackblitz.

3 Answers

The initial sorting didn't work properly when i researched it in may.

In my case it was necessary to write my own setSortHeader function that executes after getting data.

setSortHeader() {
  this.sort.active = 'break';
  this.sort.direction = 'desc';
  this.sort.sortChange.emit({ active: this.sort.active, direction: this.sort.direction });

  const sortHeader = this.sort.sortables.get('break');
  if (sortHeader) sortHeader['_setAnimationTransitionState']({ toState: 'active' });
}

I'm not sure that this code still necessary but in my case that worked.

<ng-container matColumnDef="firstName">
    <th mat-header-cell class="mat-header-sticky" id="firstName" *matHeaderCellDef mat-sort-header tabindex="0"
      [attr.aria-label]="'First Name Tool Tip'" [matTooltip]="tooltipConfiguration['firstName']"
      [matTooltipPosition]="'above'" [matTooltipClass]="'matTooltip-panel'">
      First Name
    </th>
    <td mat-cell *matCellDef="let contact" class="align-center">
      <span class="display-block overflow-ellipsis">
        {{ contact.firstName }}
      </span>
    </td>
</ng-container>

Just use same matColumnDef="firstName" and {{ contact.firstName }} notice that the property .firstName is matching with matColumnDef ID. This is as per angular docs.

Please try the below.


If you want to change the default direction, change it on the ngOnInit method call to this.sortItem

// app.component.ts

import { Component, ViewChild } from '@angular/core';

import { Sort, MatSort, MatPaginator } from '@angular/material';

import { interval } from 'rxjs';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  public displayedColumns: string[] = ['name', 'break',];

  public sortedData: any;

  constructor( ) { }

  ngOnInit() {
    setTimeout(() => {         
      this.sortItem({ active: 'break', direction: 'asc' });
    }, 1000);
  }
  compare(a: number | string, b: number | string, isAsc: boolean) {
    return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
  }

  sortItem(sort: Sort) {
    const data = DATA_SOURCE.slice();
    if (!sort.active || sort.direction === '') {
      this.sortedData = data;
      return;
    }

    this.sortedData = data.sort((a, b) => {
      const isAsc = sort.direction === 'asc';
      switch (sort.active) {
        case 'break': return this.compare(a.breakTime.start, b.breakTime.start, isAsc);        
        default: return 0;
      }
    });
  }
}

const DATA_SOURCE = [
  {
    name: 'Alice',
    breakTime: {
      start: '14-00',
      end: '14-00'
    },
  },
  {
    name: 'Steve',
    breakTime: {
      start: '10-00',
      end: '11-00'
    },
  },
  {
    name: 'Bob',
    breakTime: {
      start: '12-00',
      end: '13-00'
    },
  },
];

// app.component.html

<table mat-table [dataSource]="sortedData" multiTemplateDataRows 
       matSort (matSortChange)="sortItem($event)" 
       class="mat-elevation-z4 w-100">

  <ng-container matColumnDef="name">
    <th mat-header-cell *matHeaderCellDef> Name </th>
    <td mat-cell *matCellDef="let item"> {{item.name}} </td>
  </ng-container>

  <ng-container matColumnDef="break">
    <th mat-header-cell *matHeaderCellDef mat-sort-header > Break </th>
    <td mat-cell *matCellDef="let element">
      {{element.breakTime.start}} - {{element.breakTime.end}}
    </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let element; columns: displayedColumns;"
      class="element-row"
      [class.example-expanded-row]="expandedElement === element"
      (click)="expandedElement = expandedElement === element ? null : element">
  </tr>
</table>
Related