114
votes

How can I change Angular Material code below, so that data-table is sorted by 'name' column, ascending order by default. Arrow (indicating current sort direction) must be displayed.

This is what I want to achieve:

enter image description here

Original code:

<table matSort (matSortChange)="sortData($event)">
  <tr>
    <th mat-sort-header="name">Dessert (100g)</th>
    <th mat-sort-header="calories">Calories</th>
    <th mat-sort-header="fat">Fat (g)</th>
    <th mat-sort-header="carbs">Carbs (g)</th>
    <th mat-sort-header="protein">Protein (g)</th>
  </tr>

  <tr *ngFor="let dessert of sortedData">
    <td>{{dessert.name}}</td>
    <td>{{dessert.calories}}</td>
    <td>{{dessert.fat}}</td>
    <td>{{dessert.carbs}}</td>
    <td>{{dessert.protein}}</td>
  </tr>
</table>

I was trying something like this, but it doesn't work (no arrow displayed, not sorted)

<table matSort (matSortChange)="sortData($event)" matSortActive="name" matSortStart="asc" matSortDisableClear>

Here's link to Plunker

11
Could call this.sortData({active: "name", direction: "asc"}) on ngOnInit check plunkerPankaj Parkar
@PankajParkar It's not correct solution. Table is sorted, but Sort header doesn't know about it and arrow (indicating current sort direction) is not displayed.Jacek Kościesza

11 Answers

176
votes

You're mistaking matSortStart for matSortDirection.

Try this:

<table matSort (matSortChange)="sortData($event)" matSortActive="name" matSortDirection="asc" matSortDisableClear>

https://stackblitz.com/edit/angular-defaultsort?file=src/app/sort-overview-example.html

matSortStart can be used to reverse the cycle used when sort (e.g. when the user clicks to sort, it starts at desc instead of asc).

Edit: Thanks Ben for providing an updated example

51
votes

You can programmatically sort the table by invoking the sort(Sortable) method of the data source. Assuming you've got a dataSource component property for the data source:

// to put next to the class fields of the component
@ViewChild(MatSort) sort: MatSort

// to put where you want the sort to be programmatically triggered, for example inside ngOnInit
this.sort.sort(({ id: 'name', start: 'asc'}) as MatSortable);
this.dataSource.sort = this.sort;
20
votes
@ViewChild(MatSort) sort: MatSort;

this.dataSource.sort = this.sort;

const sortState: Sort = {active: 'name', direction: 'desc'};
this.sort.active = sortState.active;
this.sort.direction = sortState.direction;
this.sort.sortChange.emit(sortState);

should work. demo

And to show sorting direction arrow, add next css (workaround)

th.mat-header-cell .mat-sort-header-container.mat-sort-header-sorted .mat-sort-header-arrow {
    opacity: 1 !important;
    transform: translateY(0) !important;
}
10
votes

Update for Material (tested with v7.3):

@ViewChild(MatSort) matSort: MatSort;

private someMethod(): void {
  this.matSort.sort({ id: 'columnName', start: 'asc', disableClear: false });
}

This will also update the mat-sort-header's arrow without any workaround

5
votes

You can bind mat-table sort properties to you component variable also.

As @Andrew Seguin says:

<table matSort matSortActive="name" matSortDirection="asc">

This is proper way to set default sorting if you know which one is that.

In case that you get sorting from somewhere else (in my case from query string params), you can also do it like this (sorting arrows works perfectly here):

sortDirection: 'name',  // this can be changed or filled in any time
sortProperty: 'asc',


<mat-table matSort [matSortActive]="sortProperty" [matSortDirection]="sortDirection">
3
votes

There are several contributing factors that affect the behaviour. Mostly it's use of MatTableDataSource vs a hand-crafted derivative of DataSource. So different solutions may work in some cases and don't in others.

Anyway, it's an old bug that's been well covered on GitHub. Please upvote that GitHub issue to attract attention of the Angular team.

The most durable solution published on that GitHub thread (link) is to call the following method on applying a sorting order:

public setSort(id: string, start?: 'asc' | 'desc') {
    start = start || 'asc';
    const matSort = this.dataSource.sort;
    const toState = 'active';
    const disableClear = false;

    //reset state so that start is the first sort direction that you will see
    matSort.sort({ id: null, start, disableClear });
    matSort.sort({ id, start, disableClear });

    //ugly hack
    (matSort.sortables.get(id) as MatSortHeader)._setAnimationTransitionState({ toState });
}
1
votes

Maybe have you tried to call on the init of the page the sort function forced on name and direction?

     ngOnInit() {
    let defSort: Sort = {};
    defSort.direction = 'asc';
    defSort.active = 'name';
    this.sortData(defSort);
  }
1
votes

In my case sorting was not working because matColumDef id and mat-cell var is different

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

after making changes matColumnDef="firstName" to matColumnDef="name" which is same as item.name

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

it works fine for me

0
votes

I had to make default sort on load

const matSort = { id: defaultSort.name } as MatSortable;
this.sort.direction = defaultSort.sort === 'asc' ? '' : defaultSort.sort === 'desc' ? 'asc' : 'desc' as SortDirection;
this.sort.sort(matSort);
0
votes

The answer from @Andrew Seguin (first and accepted answer) did the visual trick for me, but it didn't sort the table.

My solution is to use the html code provided by @Andrew Seguin and call the sortData(sort: Sort) method myself, but how to do that? As specified in the documentation, the ,,Sort'' is an interface which hast two properties, active and direction and the interface must look something like that:

export interface Sort {
   active:string //The id/name of the column being sorted
   direction:string //asc or dsc depending on the use case (The sort direction)
}

So the trick is to call the sortData(sort: Sort) method in ngOnInit as follows:

ngOnInit(){
    //Do some nitialization
    this.sortData({active:'name', direction:'asc'});
}

sortData(sort: Sort) {
    //Your sorting algorithm (see examples in documentation, link above and at the bottom)
}

The HTML code is as in the accepted answer ;-) Hope this helps anyone, Alex

Documentation examples

0
votes

To add sort programmatically add this on your css.

::ng-deep.mat-header-cell .mat-sort-header-container.mat-sort-header-sorted .mat-sort-header-arrow { opacity: 1 !important; }