Multiple mat-table with MatSort within the same component

47,022

Solution 1

The fix to this is that after you define your ViewChild reference in the DOM your need to make sure to add the ="matSort" after it.

Steps:

  1. Set up MatSort instances in your component and define them in your DataSource dependencies like so:

    @ViewChild('hBSort') hBSort: MatSort;
    @ViewChild('sBSort') sBSort: MatSort;
    
    this.hBSource = new HBDataSource(this.hBDatabase, this.hBPaginator, 
    this.hBSort);
    this.sBSource = new SBDataSource(this.sBDatabase, this.sBPaginator, 
    this.sBSort);
    
  2. Define ViewChild References in the DOM and set them equal to matSort (Note: matSort attribute is on the mat-table tag):

    Table 1
    <mat-table #hBSort="matSort" [dataSource]="hBSource" matSort 
      style="min-width: 740px;">
                ***Table Rows and pagination***
    </mat-table>
    
    Table 2
    <mat-table #sBSort="matSort" [dataSource]="sBSource" matSort 
      style="min-width: 1200px;">
                ***Table Rows and pagination***
    </mat-table>   
    

Solution 2

In Angular 7, 9 and 10 you need both matSort and the reference #sorter1="matSort":

<table mat-table [dataSource]="ds1" matSort #sorter1="matSort">
</table>
<table mat-table [dataSource]="ds2" matSort #sorter2="matSort">
</table>

Then

@ViewChild('sorter1') sorter1: MatSort;
@ViewChild('sorter2') sorter2: MatSort;


ngOnInit() {
this.ds1.sort = this.sorter1;
this.ds2.sort = this.sorter2;
}

Solution 3

I would recommend to create a common component for table which can be used at multiple places in application. As component will create the separate instance of it, mat table did't conflicts there functionality.

In that case you don't need to repeat the code for 2 tables. Below is Table common component you can implement.

Home.component.ts

export class HomeComponent implements OnInit {
  public data1: any[];
  public data2: any[];
  constructor() {
  }
  ngOnInit() {
   this.data1 = [
    {domain: 'Hello1', gNum: 1, gPct: 'table-data1'},
    {domain: 'Hello2', gNum: 2, gPct: 'table-data2'},
    {domain: 'Hello3', gNum: 3, gPct: 'table-data3'},
    {domain: 'Hello4', gNum: 4, gPct: 'table-data4'},
    {domain: 'Hello5', gNum: 5, gPct: 'table-data5'},
    {domain: 'Hello6', gNum: 6, gPct: 'table-data6'},
    {domain: 'Hello7', gNum: 7, gPct: 'table-data7'},
   ];
   this.data2 = [
    {domain: 'Hello1', gNum: 1, gPct: 'table-data1'},
    {domain: 'Hello2', gNum: 2, gPct: 'table-data2'},
    {domain: 'Hello3', gNum: 3, gPct: 'table-data3'},
    {domain: 'Hello4', gNum: 4, gPct: 'table-data4'},
    {domain: 'Hello5', gNum: 5, gPct: 'table-data5'},
    {domain: 'Hello6', gNum: 6, gPct: 'table-data6'},
    {domain: 'Hello7', gNum: 7, gPct: 'table-data7'},
   ]
  }
}

Home.component.html

 <app-table-component [data]='data1'></app-table-component>
 <app-table-component [data]='data2'></app-table-component>

Table.component.ts

@Component({
  selector: 'app-table-component',
  templateUrl: 'table.component.html',
  styleUrls: ['table.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})

export class TableComponent implements OnInit, OnChanges {
  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;
  @Input() data: any[];
  public displayedColumns = ['domain', 'gNum', 'gPct'];
  public dataSource: MatTableDataSource<any>;

  constructor() {
  }

  public ngOnInit() {
    setTimeout(() => {
        this.dataSource = new MatTableDataSource(this.data);
        this.dataSource.paginator = this.paginator;
        this.dataSource.sort = this.sort;
   });
  }

  public ngOnChanges(changes: SimpleChanges) {
    this.dataSource = new MatTableDataSource(changes.data.currentValue);
  }

}

Table.component.html

   <mat-table #table [dataSource]="dataSource" matSort  matSortDisableClear matSortDirection="asc">
  <ng-container matColumnDef="domain">
    <mat-header-cell *matHeaderCellDef mat-sort-header>Domain </mat-header-cell>
    <mat-cell *matCellDef="let row"> {{row.domain}} </mat-cell>
  </ng-container>

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

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

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

<mat-paginator [pageSizeOptions]="[5, 10, 25, 100]"></mat-paginator>

Solution 4

Here is an Angular 6 working solution:

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

...

@ViewChild('sortCol1') sortCol1: MatSort;
@ViewChild('sortCol2') sortCol2: MatSort;

...

Data source 1:

this.dataSource1 = new MatTableDataSource(this.dataSource1);
this.dataSource1.sort = this.sortCol1;

Data source 2:

this.dataSource2 = new MatTableDataSource(this.dataSource2);
this.dataSource2.sort = this.sortCol2;

...

Table 1 (View):

<table mat-table #sortCol1="matSort" [dataSource]="dataSource1" matSort matSortActive="ID" matSortDirection="asc">
...
</table>

Table 2 (View):

<table mat-table #sortCol2="matSort" [dataSource]="dataSource2" matSort matSortActive="ID" matSortDirection="asc">
...
</table>

Solution 5

Edit:

I believe you need:

@ViewChild(MatSort) sort: MatSort;

above your:

@ViewChild('hBSort') hBSort: MatSort;
@ViewChild('sBSort') sBSort: MatSort;

Then:

ngAfterViewInit() {
    this.hBSource.sort = this.sort;
    this.sBSource.sort = this.sort;
  }

Assuming that your HBDataSource and SBDataSource both export MatTableDataSource();

I'm referencing these sources:

https://material.angular.io/components/sort/overview https://github.com/angular/material2/blob/master/src/demo-app/table/table-demo.ts

Share:
47,022
Derek J.
Author by

Derek J.

Experienced Angular developer with development done in different applications in the computer software industry. Skilled in JavaScript, TypeScript, jQuery, and SASS. I have experience working with WordPress with different themes. Also, I'm fluent in Brazilian Portuguese.

Updated on July 09, 2022

Comments

  • Derek J.
    Derek J. almost 2 years

    I have 2 material 2 tables in the same component with sorting. I cannot find a way to assign the MatSort directive to its own table. I'm only able to use MatSort on the first table and the second table doesn't recognize there is a MatSort on it. Does anyone know how to configure two tables with sorting in the same component?

    I've tried defining the ViewChild with different names, but it didn't work.

    @ViewChild('hBSort') hBSort: MatSort;
    @ViewChild('sBSort') sBSort: MatSort;
    
    
    this.hBSource = new HBDataSource(this.hBDatabase, this.hBPaginator, 
    this.hBSort);
    this.sBSource = new SBDataSource(this.sBDatabase, this.sBPaginator, 
    this.sBSort);
    
    Table 1
    const displayDataChanges = [
       this.hBPaginator.page,
       this.hBSort.sortChange,
       this._filterChange
    ];
    
    Table 2
    const displayDataChanges = [
       this.sBPaginator.page,
       this.sBSort.sortChange,
       this._filterChange
    ];
    
    Table 1
    <mat-table #hBtable [dataSource]="hBSource" matSort style="min-width: 
    740px;">
        <ng-container matColumnDef="domain">
            <mat-header-cell *matHeaderCellDef mat-sort-header> {{'list.domain' | translate}} </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.domain}} </mat-cell>
        </ng-container>
        <ng-container matColumnDef="general">
            <mat-header-cell *matHeaderCellDef mat-sort-header> {{'list.general' | translate}} </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.general.gNum}} ({{row.general.gPct | number: '1.1-2'}}%) </mat-cell>
        </ng-container>
        <mat-header-row *matHeaderRowDef="hBColumns"></mat-header-row>
        <mat-row *matRowDef="let row; columns: hBColumns;"></mat-row>
     </mat-table>
    
    
    Table 2
    <mat-table #sBSort [dataSource]="sBSource" matSort style="min-width: 1200px;">
          <ng-container matColumnDef="domain">
            <mat-header-cell *matHeaderCellDef mat-sort-header> {{'list.domain' | translate}} </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.domain}} </mat-cell>
          </ng-container>
          <ng-container matColumnDef="general">
            <mat-header-cell *matHeaderCellDef mat-sort-header> {{'list.general' | translate}} </mat-header-cell>
            <mat-cell *matCellDef="let row"> {{row.general.gNum}} ({{row.general.gPct | number: '1.1-2'}}%) </mat-cell>
          </ng-container>
          <mat-header-row *matHeaderRowDef="sBColumns"></mat-header-row>
          <mat-row *matRowDef="let row; columns: sBColumns;"></mat-row>
    </mat-table>
    
  • Derek J.
    Derek J. over 6 years
    Wouldn't that just create another reference to a sort directive, because @ViewChild(MatSort) sort: MatSort; just references the mat-table with the matSort attribute on it. I tried it, but it didn't work :(
  • Brian Wright
    Brian Wright over 6 years
    Ah, sorry, my bad. It appears what I said requires you to change how you're applying the sort to the tables. I was referencing this: material.angular.io/components/sort/overview Perhaps that might help. There is also this demo: github.com/angular/material2/blob/master/src/demo-app/table/‌​…
  • Derek J.
    Derek J. about 6 years
    I agree using a component is always better. In my case the data columns for each table were very different, so for these purposes I didn't make a component that each table could share. Although, I could make a component that has dynamic columns and data classes. Thanks for another point of view!
  • Arthur
    Arthur about 6 years
    Also, if the child is hidden on startup, you may need other tricks, see github.com/angular/material2/issues/10205
  • Derek J.
    Derek J. about 5 years
    If the tables are hidden using *ngIf, then yes because the ViewChild reference doesn't exist in the DOM. If the tables are hidden using [hidden], then it should work without any issues.
  • Kyle
    Kyle about 4 years
    This worked perfectly for me with Angular 9 too. Thanks.
  • Mary
    Mary almost 4 years
    I do not know why, I tried a different answers that look just the same, and this was that work for me. Thanks. PS> I am using angular 8
  • lwin
    lwin over 3 years
    Works at angular 10 also.
  • ANANDAN SELVAGANESAN
    ANANDAN SELVAGANESAN almost 3 years
    woks as expected :)
  • Eric Malalel
    Eric Malalel over 2 years
    well explained and works with angular 12.