Fallenreaper
Fallenreaper

Reputation: 10704

MatTable with Sort doesnt seem to sort the column

I have a simple design I was working with. In the module file, I have added the imports: MatSortModule and MatTableModule. When looking at the component I created, it was simple:

<table mat-table [dataSource]="dataSource"
       matSort>
  <ng-container matColumnDef="interface"> <!-- vvv This area may have some importance vvv -->
    <th mat-header-cell *matHeaderCellDef mat-sort-header id="interface">Interface</th>
    <td mat-cell *matCellDef="let element"><span>{{element.interface}}</span></td>
  </ng-container>
  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>

and then the TS file:

class GridComponent {
    dataSource = null
    displayedColumns = ["interface"];
    @ViewChild(MatSort, {static: true}) sort: MatSort;
    @Input() set data(val: any[]) {
      this._data = val;
      getInfo();
    }
    get data () { return this._data; }
    private _data: any[];
    ngOnInit() {
      this.dataSource = new MatTableDataSource([])
      this.dataSource.sort = this.sort
    }

    getInfo() {
      this._service.get(this._data).subscribe( data => {
          /// For the ease of understanding. assume data is defined below.
          const arr = [{"interface":"foo"},{"interface":"bar"}]
          this.dataSource = new MatTableDataSource(arr)
          this.dataSource.sort = this.sort
      });
    }
}

The grid renders all the rows, and the toggle button shows etc. When I toggle it though the information does not reorder.

Is there something I am missing? I saw some samples which said I needed to do: this.dataSource.sort = this.sort but I dont really understand that purpose and if it is somehow interfering with the sorting mechanism.

When looking at the coding sample at the bottom of: https://material.angular.io/components/sort/overview , I was confused as I didnt see any sort of ID being used, or assigned to the mat-sort-header property, but in some of the API it was stating to do that

I would love some clarification.

Edit: I am looking for a dynamic answer, one which allows me to easily change the data in the datasource. Right now, beyond the scope of the issue, I have an input which allows me to change the array of data associated with the datasource, so i am working towards keeping it encapsulated in such a way that I can handle setting properties outside of Initializers of components.

Upvotes: 4

Views: 2600

Answers (2)

Fallenreaper
Fallenreaper

Reputation: 10704

There is a built in change for matSort you can catch to handle sorting. This bypasses the: this.dataSource.sort = this.sort; lines. So you can trim all of that code out.

<table mat-table 
       [dataSource]="dataSource" 
       matSort 
       (matSortChange)="sortData($event)"></table>

and then in the code you can put:

sortData(sort: Sort){
  // Sort sorts the current list, but it wasnt updating it unless i reassigned.
  this.dataSource.data = this.dataSource.data.sort((a, b) => {
    const isAsc = sort.direction === 'asc';
    return this._compare(a[sort.active], b[sort.active], isAsc);
  });
}
private _compare(a: number | string, b: number | string, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

The other answer was not working at all for me, but now, everything flows well.

Since there are 3 states for sort.direction, 'asc', 'desc', and '', so for the empty string, I would set datasource equal to the original list order.

Here is the modified Component, with commented out areas which can now be deleted

class GridComponent {
    /// Initialize the datasource we will be augmenting its inner data.
    dataSource: MatTableDataSource<any> = new MatTableDataSource();
    displayedColumns = ["interface"];
    //@ViewChild(MatSort, {static: true}) sort: MatSort;
    @Input() set data(val: any[]) {
      this._data = val;
      getInfo();
    }
    get data () { return this._data; }
    private _data: any[];
    ngOnInit() {
      //this.dataSource = new MatTableDataSource([])
      //this.dataSource.sort = this.sort
    }

    getInfo() {
      this._service.get(this._data).subscribe( data => {
          /// For the ease of understanding. assume data is defined below.
          const arr = [{"interface":"foo"},{"interface":"bar"}]
          /// Updated the below line to just update data.
          this.dataSource.data = arr;
          //this.dataSource.sort = this.sort
      });
    }
    sortData(sort: Sort){
      // Sort sorts the current list, but it wasnt updating it unless i reassigned.
      this.dataSource.data = this.dataSource.data.sort((a, b) => {
        const isAsc = sort.direction === 'asc';
        return this._compare(a[sort.active], b[sort.active], isAsc);
      });
    }
    private _compare(a: number | string, b: number | string, isAsc: boolean) {
      return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
    }
}

Upvotes: 3

ianh11
ianh11

Reputation: 147

Yes, in order for the sort to work for the mat data table you will need to use this.dataSource.sort = this.sort within ngOnInit()

ngOnInit() {
    this.dataSource = new MatTableDataSource<FooModel>(foobar);
                this.dataSource.paginator = this.paginator;
                this.dataSource.sort = this.sort;
}

Notice how the datasource can be updated from your service subscription:

    this.fooService.getFoobar(this.isFoo).subscribe((foobar: Foo[]) => {
              if (foobars != null) {
                this.dataSource = new MatTableDataSource<Foo>(foobar);
                this.dataSource.sort = this.sort;

Upvotes: 0

Related Questions