Mat-table Sorting Demo not Working
Asked Answered
B

34

164

I am trying to get the mat-table sorting to work locally, and while I can get the data to show up as expected, clicking on the header row does not do the sorting as it does on online examples (nothing happens at all). I am trying to get this demo working locally: https://material.angular.io/components/sort/overview https://plnkr.co/edit/XF5VxOSEBxMTd9Yb3ZLA?p=preview

I have generated a new project with Angular CLI, then followed these steps: https://material.angular.io/guide/getting-started

Here are my local files:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatSort, MatTableModule } from '@angular/material';

import { AppComponent } from './app.component';
import { TableSortingExample } from './table-sorting-example';

@NgModule({
  declarations: [
    AppComponent,
    TableSortingExample,
    MatSort
  ],
  imports: [
    BrowserModule,
    MatTableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}

app.component.html

<div style="text-align:center">
  <h1>
    Welcome to {{title}}!
  </h1>
  <table-sorting-example></table-sorting-example>
</div>

table-sorting-example.html

<div class="example-container mat-elevation-z8">
  <mat-table #table [dataSource]="dataSource" matSort>

    <!--- Note that these columns can be defined in any order.
          The actual rendered columns are set as a property on the row definition" -->

    <!-- ID Column -->
    <ng-container matColumnDef="userId">
      <mat-header-cell *matHeaderCellDef mat-sort-header> ID </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.id}} </mat-cell>
    </ng-container>

    <!-- Progress Column -->
    <ng-container matColumnDef="progress">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Progress </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.progress}}% </mat-cell>
    </ng-container>

    <!-- Name Column -->
    <ng-container matColumnDef="userName">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
    </ng-container>

    <!-- Color Column -->
    <ng-container matColumnDef="color">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Color </mat-header-cell>
      <mat-cell *matCellDef="let row" [style.color]="row.color"> {{row.color}} </mat-cell>
    </ng-container>

    <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
  </mat-table>
</div>


<!-- Copyright 2017 Google Inc. All Rights Reserved.
    Use of this source code is governed by an MIT-style license that
    can be found in the LICENSE file at http://angular.io/license -->

table-sorting-example.ts

import {Component, ViewChild} from '@angular/core';
import {DataSource} from '@angular/cdk/collections';
import {MatSort} from '@angular/material';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/startWith';
import 'rxjs/add/observable/merge';
import 'rxjs/add/operator/map';

/**
 * @title Table with sorting
 */
@Component({
  selector: 'table-sorting-example',
  styleUrls: ['table-sorting-example.css'],
  templateUrl: 'table-sorting-example.html',
})
export class TableSortingExample {
  displayedColumns = ['userId', 'userName', 'progress', 'color'];
  exampleDatabase = new ExampleDatabase();
  dataSource: ExampleDataSource | null;

  @ViewChild(MatSort) sort: MatSort;

  ngOnInit() {
    this.dataSource = new ExampleDataSource(this.exampleDatabase, this.sort);
  }
}

/** Constants used to fill up our data base. */
const COLORS = ['maroon', 'red', 'orange', 'yellow', 'olive', 'green', 'purple',
  'fuchsia', 'lime', 'teal', 'aqua', 'blue', 'navy', 'black', 'gray'];
const NAMES = ['Maia', 'Asher', 'Olivia', 'Atticus', 'Amelia', 'Jack',
  'Charlotte', 'Theodore', 'Isla', 'Oliver', 'Isabella', 'Jasper',
  'Cora', 'Levi', 'Violet', 'Arthur', 'Mia', 'Thomas', 'Elizabeth'];

export interface UserData {
  id: string;
  name: string;
  progress: string;
  color: string;
}

/** An example database that the data source uses to retrieve data for the table. */
export class ExampleDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<UserData[]> = new BehaviorSubject<UserData[]>([]);
  get data(): UserData[] { return this.dataChange.value; }

  constructor() {
    // Fill up the database with 100 users.
    for (let i = 0; i < 100; i++) { this.addUser(); }
  }

  /** Adds a new user to the database. */
  addUser() {
    const copiedData = this.data.slice();
    copiedData.push(this.createNewUser());
    this.dataChange.next(copiedData);
  }

  /** Builds and returns a new User. */
  private createNewUser() {
    const name =
      NAMES[Math.round(Math.random() * (NAMES.length - 1))] + ' ' +
      NAMES[Math.round(Math.random() * (NAMES.length - 1))].charAt(0) + '.';

    return {
      id: (this.data.length + 1).toString(),
      name: name,
      progress: Math.round(Math.random() * 100).toString(),
      color: COLORS[Math.round(Math.random() * (COLORS.length - 1))]
    };
  }
}

/**
 * Data source to provide what data should be rendered in the table. Note that the data source
 * can retrieve its data in any way. In this case, the data source is provided a reference
 * to a common data base, ExampleDatabase. It is not the data source's responsibility to manage
 * the underlying data. Instead, it only needs to take the data and send the table exactly what
 * should be rendered.
 */
export class ExampleDataSource extends DataSource<any> {
  constructor(private _exampleDatabase: ExampleDatabase, private _sort: MatSort) {
    super();
  }

  /** Connect function called by the table to retrieve one stream containing the data to render. */
  connect(): Observable<UserData[]> {
    const displayDataChanges = [
      this._exampleDatabase.dataChange,
      this._sort.sortChange,
    ];

    return Observable.merge(...displayDataChanges).map(() => {
      return this.getSortedData();
    });
  }

  disconnect() {}

  /** Returns a sorted copy of the database data. */
  getSortedData(): UserData[] {
    const data = this._exampleDatabase.data.slice();
    if (!this._sort.active || this._sort.direction == '') { return data; }

    return data.sort((a, b) => {
      let propertyA: number|string = '';
      let propertyB: number|string = '';

      switch (this._sort.active) {
        case 'userId': [propertyA, propertyB] = [a.id, b.id]; break;
        case 'userName': [propertyA, propertyB] = [a.name, b.name]; break;
        case 'progress': [propertyA, propertyB] = [a.progress, b.progress]; break;
        case 'color': [propertyA, propertyB] = [a.color, b.color]; break;
      }

      let valueA = isNaN(+propertyA) ? propertyA : +propertyA;
      let valueB = isNaN(+propertyB) ? propertyB : +propertyB;

      return (valueA < valueB ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1);
    });
  }
}


/**  Copyright 2017 Google Inc. All Rights Reserved.
 Use of this source code is governed by an MIT-style license that
 can be found in the LICENSE file at http://angular.io/license */

Does anyone have an idea of why it would show up like the online table but lack the sorting functionality?

Bonesetter answered 23/10, 2017 at 15:33 Comment(2)
I would debug the app first. Any errors? run the ng test --sm=false and see what's come out.Subterranean
It is working for me without @ViewChild(MatSort) sort: MatSort; Any reason ?Punster
B
297

For anyone else who may have this problem: The problem was I didn't read the API reference properly on the angular materials website, the part that said I had to import MatSortModule. After I changed my imports list in app.module.ts to

imports: [
    BrowserModule,
    MatTableModule,
    MatSortModule
  ],

it worked fine

Bonesetter answered 24/10, 2017 at 14:3 Comment(11)
there is no mention of this module in documentation. material.angular.io/components/table/overview#sorting i wasted an hour on this as well.Simard
@SonicSoul you are right, by reading the API properly I meant looking at the sort module API (material.angular.io/components/sort/api). I do agree that it should be noted on the table overview as well thoughBonesetter
this is fine, in the header text is clickable and the icon is also there but still sorting is not working.Fairish
Check if BrowserAnimationsModule is also imported in app.module.tsFado
Can I say they're SOBs? I spent 1 hour trying to figure out why my ViewChild wasn't working. Can't they import/export this MatSortModule from the MatTableModule??Senegambia
cool answer, kinda below mentioned answer also helped ...thanksTymbal
I've imported the MatSortModule and BrowserAnimationsModule, and i've ensured that the matColumnDef value matches the property name, yet I'm still unable to get it to do anything.Jungjungfrau
It Worked for me the Documentation Lacks the instructions...!! Many Many thanks Dude.Wortham
Funny thing is that WebStorm does not see this (the lack of import) as a mistake as well...Maker
Works for me, I didn't see this anywhere in the documentation, tksCufic
I had to set the sort property of the datasource after setting the data for it to work. Oddly enough, the paginator doesn't require this 'trick'. this.dataSource.data = ...; this.dataSource.sort = this.sort; // after setting the dataKavita
R
234

I had a problem that the sorting function was working but it wasn't sorting properly. I realized that matColumnDef has to have the same name of the property of my class / interface that I am referencing in matCellDef.

According to the Angular Material documentation:

By default, the MatTableDataSource sorts with the assumption that the sorted column's name matches the data property name that the column displays.

For exemple:

<ng-container matColumnDef="name"> 
    <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
    <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
</ng-container>

The name in the matColumnDef directive has to be the same as the name used in the <mat-cell> component.

Robers answered 29/1, 2018 at 20:23 Comment(8)
What in your example are you referencing? It would be helpful to see your interface as well, for comparison.Celestine
I was using "Id" as name of column whereas entity was having "id". The case difference was making it not run (due to a refactoring miss).. Now its solved. ThanksCannikin
@NitinSingh, what if you need call a function on the element, like this ` {{row.getName()}} `Alt
I need to access property 'name' from another property 'meeting', how do I so that?Spiceberry
@Suyog, https://mcmap.net/q/151585/-angular-material-2-datatable-sorting-with-nested-objects.Ruthful
Oh my god. This is such a.. weird way of handling things. Worked for me though! Spent an hour trying to figure out what was wrong, thanks!Beaudoin
I am happy that this solution still works!Robers
freaking A this was my issue I was going crazy lol thanks!!Charmeuse
S
218

If the table is inside *ngIf, it won't be working. It will work if it is changed to [hidden]

Shadowgraph answered 14/5, 2019 at 9:46 Comment(4)
Or just set the data source in ngAfterViewInit instead of ngOnInitLeatri
The solution worked for me. Thanks, @Shadowgraph but, just curious to know what triggers this issue with *ngIf, any idea guys?Gurney
he-difference-between-ngif-and-hidden https://mcmap.net/q/151586/-what-is-the-difference-between-ngif-and-hiddenSherwin
[hidden] still reserves space for the table so it won't be a good solution most of the timeNilla
N
64

One of the reasons MatSort might not work, is when it is added to a dataSource (i.e. this.dataSource.sort = this.sort) before it is defined. There can be multiple reasons for this:

  1. if you add the sort in ngOnInit. At this point the template is not yet rendered, so the MatSort you get with @ViewChild(MatSort, { static: true }) sort: MatSort; is undefined and understandably will not do anything. A solution for this issue is to move this.dataSource.sort = sort to ngAfterViewInit. When ngAfterViewInit is called your component is rendered, and MatSort should be defined.

  2. when you use *ngIf is your template on your table element or one if it's parent elements and this *ngIf causes your table not to be rendered at the moment you try to set the MatSort. For example, if you have *ngIf="dataSource.data.length > 0" on your table element (to only render it if there is data present) and you set this.dataSource.sort = this.sort right after you set this.dataSource.data with your data. The component view will not be rerendered yet, so MatSort will still be undefined.

In order to get MatSort to work and still conditionally show your table you could decide to replace the *ngIf with [hidden] as stated in multiple other answers. However, if you want to keep your *ngIf statement you can use the following solution. This solution works for Angular 9, I haven't tested it on previous versions so I'm not sure if it works there.

I found this solution here: https://github.com/angular/components/issues/10205

Instead of putting:

@ViewChild(MatSort) sort: MatSort;

use a setter for matSort. This setter will fire once matSort in your view changes (i.e. is defined the first time), it will not fire when you change your sorting by clicking on the arrows. This will look like this:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    this.dataSource.sort = sort;
}

If you have other functions that (programmatically) change the sorting, I'm not sure if it will fire again, I haven't tested this. If you wan't to make sure it only sets the sort if the sort was undefined, you can do something like this:

@ViewChild(MatSort) set matSort(sort: MatSort) {
    if (!this.dataSource.sort) {
        this.dataSource.sort = sort;
    }
}
Nealson answered 28/7, 2020 at 15:49 Comment(5)
Thank you, this solution is perfect. Just a note, if you have a *ngIf for your table (waiting for data from an API, for example), you need to add a conditional to check if "sort" exists. If you don't, you will get an error on console "Cannot set property 'sort' of undefined".Draco
This solution seems to work for me, however I still have the console errors after checking if sort exists on the table in the template.Misestimate
Most explanatory answer here, worked like a charm for me, thanks a lot ! I may add that to avoid the "Cannot set property 'sort' of undefined", you can add a simple check of "sort" at the beginning of the setter, I.E "if (!sort) { return; }"Hueyhuff
This worked for me on all columns except the one with an Id of a number. All string columns gets sorted perfectly. Any idea how I can manage to allow it to sort the number column as well though?Thee
Wow dude, God bless you. This was exactly what I needed to find. I have this same concept working for a paginator as well now. Thank you!!!Timmytimocracy
C
48

matColumnDef name and *matCellDef actual value name should be same

Example:

<ng-container matColumnDef="oppNo">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Opportunity Number</th>
    <td mat-cell *matCellDef="let element">{{element.oppNo}}</td>
</ng-container>

In my case oppNo is same for matColumnDef name and *matCellDef name and sorting working fine.

Catenane answered 3/12, 2018 at 7:32 Comment(2)
Interesting. That was the case for me as well. But, do you know the actual reasoning behind this or that's actually some sort of a "bug"?Collyrium
I found out, that the reason is, that it takes this key inside the dataSource to sort. So it does not need to be 1to1 the same like matCellDef, but you need a reference-property inside dataSource.Jonathonjonati
K
40

I also hit this issue. Since you need to wait for the child to be defined, you have to implement and use AfterViewInit, not onInit.

  ngAfterViewInit (){
    this.dataSource.sort = this.sort;
  }
Kitchenmaid answered 8/3, 2019 at 21:25 Comment(2)
I'm using a table with sorting, filtering and pagination. Do you have any clue why only the sorting has to be defined in ngAfterViewInit ? The rest was working from ngOnInit. It's just to try to understand, it's fixed thanks to youBoschbok
@NicolasM. The this.sort field is bound via @ViewChild annotation and such fields are instantiated in ngAfterViewInit and not yet in ngOnInitAerify
S
28

Adding sort within timeout block works for me,

dataSource = new MatTableDataSource(this.articleService.getAllArticles());
setTimeout(() => {
  this.tableDataSource.sort = this.sort;
  this.tableDataSource.paginator = this.paginator;
});

If you don't want to use lifecykle hooks.

Said answered 31/12, 2018 at 17:16 Comment(2)
stupid hack but it works, any idea why it doesn't work without the timeout?Merca
Really a bad way of doing it. Itworks because you are letting some time pass after initialization of component so that dataSource is built, and then you add sort and paginator. The best is to move datSource buidling in ngOnInit then move sort and paginator assigments in AfterViewInit. That is what Lifecycle hooks exist for.Conduce
P
16

I spent hours on this issue. After reading through a number of threads, here are the steps I did.

  1. As @avern mentioned, you need to import MatSortModule.
  2. Make sure you are NOT enclosing the table in a *ngIf. Change it to [hidden] as @zerg recommended. (I don't understand why)

Hope this helps.

Peterman answered 27/1, 2020 at 14:26 Comment(0)
C
14

My solution was to fix several things (basically merging most of the solutions in this page).

Things to check:

  1. BrowserModule, MatTableModule, MatSortModule Modules should be imported in the root modules file.
  2. Make sure to have used MatTableDatasource class and pass your data array in it as a parameter
  3. Make sure your table is not nested in an *ngIf=.... directive. Use other conditional operations instead (still don't understand why).
Clausen answered 29/8, 2019 at 8:28 Comment(0)
P
12

in your app.module.ts, do the following:

import

import { MatSortModule } from '@angular/material/sort';

then add

imports: [
    ...
    MatSortModule
],
Pew answered 5/4, 2021 at 11:31 Comment(0)
W
7

I found this old blog which helped me get it to work: https://www.jeffryhouser.com/index.cfm/2018/10/23/Five-Reasons-My-ngMaterial-Table-wont-sort

  1. Make sure to import MatSortModule
  2. Specify the matSort header
  3. Make sure to wrap your datasource in a MatTableDataSource
    • This is the one that helped me sort it out (get it? sort it out). In the template I was referring to the array directly (<table mat-table [dataSource]="this.products" matSort>) but I should have used the datasource object I initialized in the code (<table mat-table [dataSource]="this.dataSource" matSort>). The datasource is initialized like dataSource = new MatTableDataSource(this.products)
  4. Tell the data source about your sort, in ngOnInit/ngAfterViewInit
  5. Write your own sort, if you do not want to use MatTableDataSource
Wilsonwilt answered 3/7, 2020 at 6:11 Comment(0)
S
6

I fixed this in my scenario by naming the table data with same name as *matColumnDef For example:

<!-- Name Column -->
<ng-container matColumnDef="name">
  <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
  <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
</ng-container>

Instead

<!-- Name Column -->
    <ng-container matColumnDef="userName">
      <mat-header-cell *matHeaderCellDef mat-sort-header> Name </mat-header-cell>
      <mat-cell *matCellDef="let row"> {{row.name}} </mat-cell>
    </ng-container>
Stingy answered 5/11, 2019 at 12:18 Comment(1)
This helped fix it for me. However, it turns out it's also CASE SENSITIVE. So matColumnDef="name" must also match the case of row.name.Meliorate
D
6

After spending time on this for weeks. I found out you the following

  1. You need to import MatSortModule in your app.module.ts.
 imports: [
    ...
    MatSortModule
],
<ng-container matColumnDef="ledgerTransactionCreditSum">
        <th mat-header-cell *matHeaderCellDef mat-sort-header> Ledger Transaction Credit </th>
        <td mat-cell *matCellDef="let element"> {{element.ledgerTransactionCreditSum}} </td>
    </ng-container>
 matColumnDef and element.ledgerTransactionCreditSum variable and matcolumn def shoulde be same
  1. Define the sort and paginator in ngViewInit
ngAfterViewInit(): void {
    this.dataSource.sort = this.sort;
    this.dataSource.paginator = this.paginator;
    this.dataSource.paginator?._changePageSize(400)
  }
Dizon answered 8/2, 2022 at 14:3 Comment(0)
D
5

There were 2 issues for me.

  1. The matColumnDef and matCellDef -> names are different
  2. I was getting the data from the service. The ngOnInit sort was not working. Replaced with

    ngAfterViewInit() { this.dataSource.sort = this.sort; }

Dried answered 12/3, 2020 at 17:40 Comment(0)
S
5

For anyone that is confused about these namings having to be equal, I did some testing:

This will work (the name of the property is the same as the column def):

<ng-container matColumnDef="version">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version']

This will NOT work (the name of the property is not the same as the column def):

<ng-container matColumnDef="version2">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.version}} </td>
</ng-container>

displayedColumns: string[] = ['version2']

Fyi, this also does NOT work (the length of a property):

<ng-container matColumnDef="length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['length']

And neither does this:

<ng-container matColumnDef="ids.length">
    <th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
    <td mat-cell *matCellDef="let element"> {{element.ids.length}} </td>
</ng-container>

displayedColumns: string[] = ['ids.length']
Studer answered 13/9, 2020 at 3:23 Comment(2)
In the case of the third option {{element.ids.length}}, is there a solution if the data is already modeled that way?Bruckner
@PacoZevallos I think the only way you can solve it is by setting the property on the main object, something like element.ids_length = element.ids.lengthStuder
M
5

The main reasons why mat-sort & mat-paginator doesn't work are

  1. Modules MatSortModule & MatPaginatorModule are not imported
  2. Table is under *ngIf condition
  3. matColumnDef should be the same as matCellDef and displayedColumns array.
Machos answered 24/9, 2021 at 6:25 Comment(0)
I
4

For me replacing *ngIf with [hidden] attribute for mat-table tag worked. How to post this one as a bug to Angular Material community?

Imbrication answered 19/9, 2019 at 1:5 Comment(0)
L
4

The below code was worked for me perfectly,

@ViewChild(MatSort) set matSort(sort: MatSort) {
if (!this.dataSource.sort) {this.dataSource.sort = sort;}}
Langlauf answered 6/8, 2021 at 7:54 Comment(1)
This worked for me also. After two days of debugging, testing and changing different things, still don't know why, but this solution works. Thanks!Biradial
C
3

If your table is inside an *ngIf and you think it has something to do with it not sorting your table, then specifying your own sortingDataAccessor function might solve the issue as it did for me. I have my table inside couple of *ngIfs and taking it out of those *ngIfs did not make sense:

`ngAfterViewInit(): void {
        this.matchesDataSource.sort = this.sort;
        this.matchesDataSource.sortingDataAccessor = previewMatchSortingFn;
    }`

`export function previewMatchSortingFn(item: Match, header: string): string | number {
    switch (header) {
        case 'home':
            return item.homeTeam.name;
        case 'away':
            return item.awayTeam.name;
        case 'date':
            if (item.dateTime) {
                // this will return the number representation of the date
                return item.dateTime.valueOf();
            }
            return;
        default:
            break;
    }
}`
Carding answered 21/12, 2019 at 21:17 Comment(0)
C
3

I found multiple answers to this question, but implementing them individually did not give me any results. So I tried to merge answers and it worked.

Firstly, I added ViewChild sort inside NgAfterViewInit interface. (It was initially inside a function that was called through NgOnInit

 ngAfterViewInit(){
    this.tableData.sort = this.sort;
  }

For the second step i changed the *ngIf inside a container to [hidden]. I do get an error that says that the value is not loaded. But it is not a major one to be concerned about till now.

before

<div class="mat-elevation-z0 container-fluid" *ngIf={some boolean resultant condition}>

after

<div class="mat-elevation-z0 container-fluid" [hidden] = {negation of your boolean expression}>

psst.. You can also consider adding a loading spinner while your table is being loaded through mat-footer to above the above bs.

    <ng-container matColumnDef="loading">
                    <mat-footer-cell *matFooterCellDef colspan=6>
                        <div class="uploader-status">
                            <mat-spinner strokeWidth="25" [diameter]="100" title="Server Starting" ></mat-spinner>
                        </div>
                    </mat-footer-cell>
</ng-container>


<mat-footer-row *matFooterRowDef="['loading']" [ngStyle]="{'display': (this.candidateService.candidateRecords!=null) ? 'none':'block'}"></mat-footer-row>
Cresida answered 3/11, 2020 at 13:39 Comment(0)
G
1

In addition to all the previous answers, sometimes the table isn't visible at the time of data retrieval. For example, I had to display a table with MatSort and MatPaginator inside of a modal/dialog. Therefore, I had to pass in the elements through their respective Output emitter functions like so:

<... matSort #sort="matSort" (matSortChange)="sortData(sort)">
<... #paginator (page)="changePaginator(paginator)">

And in the typescript:

  @ViewChild(MatSort, { static: false }) set sort(s: MatSort) {
      this.dataSource.sort = s;
  }
  @ViewChild(MatPaginator, { static: false }) set paginator(p: MatPaginator) {
      this.dataSource.paginator = p;
  }

  sortData(sort: MatSort) {
      this.sort = sort;
  }
  changePaginator(paginator: MatPaginator) {
      this.paginator = paginator;
  }

Make sure to set the paginator's default values in its input directives since the above code will set the elements after paginating, i.e: [pageSize]="5" [length]="dataSource?.data?.length". Please use this as a last resort to all previous solutions.

Goffer answered 22/12, 2020 at 15:56 Comment(0)
C
1

Its working for me..

@ViewChild('sortConfigTable', { static: false }) sortConfigTable: MatSort;

after data initial assign

      this.dataSource.data = ds;

      setTimeout(() => {
        if (this.sortConfigTable) {
          this.dataSource.sort = this.sortConfigTable;
        }
      }, 1000);
Chromite answered 12/5, 2022 at 21:5 Comment(0)
A
0

Actually matColumnDef name(i.e column name) and your Class/Interface property name should be equal to make it work.

Sometimes we can't change our Class/Interface property name, in that case, we can implement custom sorting as below.

let say your columns  as  ['id', 'name'] and 
your class/interface  as  ['userId', 'name']

if we perform sorting on the 'id' column it won't work. Try with the custom sorting

this.dataSource.sortingDataAccessor = (item,property)=>{

 // where item is your class/interface data
 // where property is your column name

       switch(property){
           case 'id' : return item.userId
           default: return item[property];
        }
}
Ate answered 27/8, 2020 at 6:47 Comment(0)
S
0

If you read all answers until here and nothing helped, maybe you have the same problem I had.

The problem was that my MatTableDataSource object

dataSource = new MatTableDataSource<StbElement>(ELEMENT_DATA);

Was used in the html file without this.

Changing:

<table mat-table [dataSource]="dataSource" matSort class="mat-elevation-z8">

To:

<table mat-table [dataSource]="this.dataSource" matSort class="mat-elevation-z8">

Fixed the problem.

Sexology answered 1/11, 2020 at 12:54 Comment(1)
Impossible. Has nothing to do with this, unless the Material Table contains a bug.Gattis
U
0
My solution for this problem is as below - 


1. These two lines will go in the same order.

    this.dataSource = new MatTableDataSource(myRowDataArray);// this dataSource is used in table tag.
    this.dataSource.sort = this.sort;


2. Pass MatTableDataSource object in [dataSource] 
    <table mat-table [dataSource]="dataSource">
    // rest of the table definition here
    </table>

3. By default, the MatTableDataSource sorts with the assumption that the sorted column's name matches the data property name that the column displays.

Example - 
    <ng-container matColumnDef="date" >
          <th class="headers" mat-header-cell  *matHeaderCellDef mat-sort-header>Date</th>
          <td class="data" mat-cell *matCellDef="let row">{{row.date|date}}</td>
    </ng-container>

4. If the table is inside *ngIf,then replace it with [hidden] or some other filter.

I missed the 2nd point.

Cheers!
Uda answered 24/11, 2020 at 8:14 Comment(0)
T
0

This issue mainly happens when sort is initialized before the dataSource. In the demo found here the dataSource is initialized statically as a result no issue happens. When you have to fetch data asynchronously, though, you need to wait for the response from API call to reach and get assigned to the dataSource before you initilize the sort instance variable.

Tripartition answered 1/5, 2021 at 18:20 Comment(0)
C
0

I don't know the reason; but putting this.dataSource.sort = this.sort; assignment to ngAfterViewInit() method didn't work. Even I confirmed this function is getting hitting after page loaded still was not working. The solution for me was putting sort assignment in ngOnInit() method.

 ngOnInit(): void {
   this.service.getAllAudits().subscribe(result => { 
    this.dataSource  = new MatTableDataSource(result); 
    this.dataSource.sort = this.sort; 
  });

}

Curson answered 13/7, 2021 at 8:36 Comment(0)
B
0

changing

@ViewChild('matsort') sort: MatSort;

to

@ViewChild(matSort) sort: MatSort

did it for me must be the same

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8" matSort matSortActive="name" matSortDirection="asc">
Basilius answered 6/2, 2022 at 12:23 Comment(0)
B
0

when you assign dataSource.sort = this.sort, you should keep this line inside ngAfterViewInit and not inside ngOnInit.

I had the same issue. Once I did above it started working fine.

Bluefarb answered 2/10, 2022 at 15:12 Comment(1)
flashshape already mentioned this in his answerTelemotor
I
0

There could be couple of reasons behind that, make sure you follow the documentation correctly and import all required modules but if the error still persists, then followed could be the reasons.

  1. Make sure you add matSort directive on table and mat-sort-header directive on the cell.

  2. If you table is in the *ngIf, sorting and pagination won't work either.

      <table mat-table [dataSource]="dataSource" matSort (matSortChange)="announceSortChange($event)">
      <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription="Sort by No">
    
Imphal answered 13/1, 2023 at 4:2 Comment(0)
A
0

All of the above did not help me. I receive such dates as "01/20/2020 01:20:30.400 PM" Here is the solution that helped me:

Added new Date(item[property]) creation:

this.yourDataSource.sortingDataAccessor = (item: any, property) => {
    if (property === 'date') {
        return new Date(item[property]);
    }
    return item[property];
};

It works for me!

Analyst answered 30/3, 2023 at 9:43 Comment(0)
P
0

Brother, follow this video. It is working for me now. The name of the displayed column needs to match the name of the field to be sorted. Example:

https://www.youtube.com/watch?v=Rpem1hN-WrU&ab_channel=KnowledgeBase

model

export interface Ticket {
  order: number;
}

displayed columns

public displayedColumns: string[] = ['order'];

HTML

<ng-container matColumnDef="order">
                <th mat-header-cell *matHeaderCellDef mat-sort-header>Pedido</th>
                <td mat-cell *matCellDef="let element" data-label="order">{{element.order}}</td>
              </ng-container>
Pretend answered 11/4, 2023 at 22:50 Comment(0)
V
0

Simple fix! When your table is within *ngIf="dataSource.data?.length" you have to replace *ngIf to [ngStyle] like below:

[ngStyle]="{ display: dataSource.data?.length ? 'block' : 'none' }"
Vagabondage answered 15/11, 2023 at 14:52 Comment(0)
F
-1

See if you have any javascript errors in the console. It could be that some other thing failed before your sorting initialized.

Firetrap answered 26/9, 2019 at 5:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.