154

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

Vega
  • 27,856
  • 27
  • 95
  • 103
Jacek Kościesza
  • 1,782
  • 2
  • 12
  • 11
  • 1
    Could call `this.sortData({active: "name", direction: "asc"})` on `ngOnInit` check [plunker](https://plnkr.co/edit/hsIXe6eKUX5RmkyVdrdw?p=preview) – Pankaj Parkar Oct 14 '17 at 10:13
  • 1
    @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 Oct 14 '17 at 12:02

13 Answers13

231

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

Andrew Seguin
  • 3,042
  • 1
  • 12
  • 9
  • 7
    This method working only for the first time. After the table's dataSource was changed, I try to re-set the `matSortActive` and `matSortDirection` but the small arrow isn't displayed – Gil Epshtain Aug 06 '18 at 15:36
  • 1
    The sample seems not working anymore, I made a new one: https://stackblitz.com/edit/angular-defaultsort?file=src/app/sort-overview-example.html – Ben May 22 '20 at 03:08
  • You can trigger sorting and fix the hidden arrow bug with [this solution](https://github.com/angular/components/issues/10242#issuecomment-470726829). I'm using mat-sort-header without a data source, and this was the only fix that worked. – Stevethemacguy Mar 19 '21 at 21:34
  • @GilEpshtain to fix this, use @Aman Madiiarbekov`s answer below. – Tobias Kaufmann Apr 24 '21 at 09:10
  • Hello, if my table implements pagination, how can I ensure that the ``active`` and ``direction`` parameters are carried over to the next page? – LondonMassive Jul 03 '21 at 13:49
  • Not working in your provided stackblitz also – Deviprasad Sharma Aug 05 '21 at 05:07
65

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;
David Wolf
  • 1,400
  • 1
  • 9
  • 18
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
  • 2
    This is kind of what I'm looking for but the only issue is it triggers the `matSortChange` event. Is there a way to set sort without triggering the event? – rain01 Jan 25 '19 at 22:55
  • No. That is how the sort gets called. Why don't you want the matSortChange event to be triggered? – Nino Filiu Jan 26 '19 at 09:32
  • 1
    because I have it set to update a cookie with `asc`/`desc` of a column and if it is called each time page is loaded then every time it will be different – rain01 Apr 17 '19 at 22:50
  • If you're using mat-sort-header *without* a data source, you can trigger sorting programmatically with this solution: https://github.com/angular/components/issues/10242#issuecomment-470726829 – Stevethemacguy Mar 19 '21 at 21:29
  • This only worked the first time for me, after initializing the table another time the order was wrong. The answer below from @Aman Madiiarbekov did the job for me. – Tobias Kaufmann Apr 24 '21 at 09:09
  • @TobiasKaufmann - For me, I am not having that issue. Whether I reload the page with a browser refresh or a route change, the column sorting always happens as specified – Ste Sep 30 '21 at 14:38
  • I needed to drop this in AfterViewInit instead of OnInit, or order for the ViewChild to be populated. – Gopherkhan Oct 05 '22 at 20:32
28
@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;
}
  • 4
    Please provide some explanations along with your code, so that later user can follow your ideas/code more easily. – HansHirse Apr 24 '19 at 05:07
  • 1
    I used the sort from [Nino](https://stackoverflow.com/a/51787040/715342) and the CSS workaround here to get my programmatically-set sort to show the arrow. – bts May 29 '19 at 14:27
  • In angular 7 I just set the this.sort = { active: 'name', direction: 'desc' }; and I did not have to add any CSS changes for the arrow to be active. – Nick Gallimore Aug 09 '19 at 18:17
  • Nick Gallimore maybe you are adding your css not in correct place? try to add it in a main global css file (it can be in the assets/css/...css) – Aman Madiiarbekov Aug 19 '19 at 03:12
  • If you don't want to use !important, you can also fix the hidden arrow with JS: https://github.com/angular/components/issues/10242#issuecomment-470726829 – Stevethemacguy Mar 19 '21 at 21:37
  • I am just wondering how did use `sort` var of `ViewChild` in `ngOnInit` – sql_dummy Apr 14 '21 at 07:45
11

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

sschmid
  • 1,341
  • 1
  • 16
  • 22
8

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">
Milos
  • 1,678
  • 4
  • 23
  • 49
4

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 });
}
Alex Klaus
  • 8,168
  • 8
  • 71
  • 87
  • 1
    this should be the accepted solution for changing the sort programmatically !! ty so much sir ! – que1326 Jun 28 '21 at 10:32
2

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

Yogesh Borkhade
  • 616
  • 5
  • 10
1

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

Alex B
  • 15
  • 7
0

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);
  }
federico scamuzzi
  • 3,708
  • 1
  • 17
  • 24
  • 7
    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 Oct 14 '17 at 12:06
0

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);
Illia
  • 1
  • 1
0

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; }

0

You can add the following code

   ngAfterViewInit{
          this.sort.disableClear = true;
          this.sort.sort({disableClear: true, id:sortEnabledColumns.id,start:'asc'})
   }

Without the first line it will go to default hide status when you click third time

Ishan Fernando
  • 2,758
  • 1
  • 31
  • 38
0
I found this in Github, works fine for me
      
 @ViewChild(MatSort) sort: MatSort;
     //reset state so that start is the first sort direction that you will see
    this.sort.sort({ id: null, start: 'desc', disableClear: true });
    this.sort.sort({ id: 'FieldNameToSort', start: 'asc', disableClear: true });
    this.sort.sortChange.emit(this.sort);
    (this.sort.sortables.get('FieldNameToSort') as MatSortHeader)._setAnimationTransitionState({ toState: 'active' });
Raghu
  • 1
  • 1
  • 1
    Welcome to SO! Please take a look at the other answers that were given before. Your approach is mentioned there already. In order to keep the site clear and make it easy to find answers, we try to avoid double answers. – ahuemmer Nov 19 '22 at 10:05