24

I try to create a component which includes two dataTables each of it with another dataSource. My Tables aren't visible right after the component is loaded because of my *ngIf so that i couldn't use ngAfterViewInit() instead i'm using a solution a user pointed out on Github:

  private paginator: MatPaginator;
  private reportingPaginator: MatPaginator;
  private sort: MatSort;
  private reportingSort: MatSort;

  @ViewChild(MatSort) set matSort(ms: MatSort) {
    this.sort = ms;
    this.reportingSort = ms;
    this.setDataSourceAttributes();
  }

  @ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
    this.paginator = mp;
    this.reportingPaginator = mp;
    this.setDataSourceAttributes();
  }

  setDataSourceAttributes() {

      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;

      this.reportingDataSource.paginator = this.reportingPaginator;
      this.reportingDataSource.sort = this.reportingSort;

}

But i still can't get it to work. My Pagination doesn't work, when both paginators are included into the @ViewChild(MatPaginator). If i only include one of the paginators

@ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
    this.reportingPaginator = mp;
    this.setDataSourceAttributes();
}

or

@ViewChild(MatPaginator) set matPaginator(mp: MatPaginator) {
    this.paginator = mp;
    this.setDataSourceAttributes();
}

the one i included works fine! So what do i need to do to get both paginators work?

Sithys
  • 3,655
  • 8
  • 32
  • 67
  • Do you need to use [`@ViewChildren`](https://angular.io/api/core/ViewChildren) in this case? – R. Richards May 19 '18 at 20:17
  • How to solve that with ViewChildren? :( I cant figure it out... i thought about just instantiating it all the correct way, so that booth are working inside the viewChild. If i only put one into it, it works. – Sithys May 19 '18 at 21:59
  • If you have more than one `MatPaginator` on the page, then [`@ViewChildren(MatPaginator)`](https://angular.io/api/core/ViewChildren) should give you [`QueryList`](https://angular.io/api/core/QueryList), according to the [docs](https://angular.io/api/core/ViewChildren). What you do with that list depends on what you are trying to do in the end. – R. Richards May 19 '18 at 22:06

2 Answers2

47

For mutiple MatPaginator and MatSort components present in single page you need yo use

@ViewChildren(MatPaginator) paginator = new QueryList<MatPaginator>();
@ViewChildren(MatSort) sort = new QueryList<MatSort>();

in your code which will return you list of MatSort and MatPaginator defined in the order in which it is present int your page. Below is the complete implemetation

import { Component, OnInit, ViewChild, ViewChildren, AfterViewInit, QueryList } from '@angular/core';
import { MatTableDataSource, MatSort, MatPaginator } from '@angular/material';

export interface AssignmentElement {
  assignmentId: number;
  action: string;
  userName: string;
  roleName: string;
  enabled: string;
  createdOn: string;
  createdBy: string;
  modifiedOn: string;
  modifiedBy: string;
  status: string;
}

export interface RoleElement {
  roleId: number;
  action: string;
  roleName: string;
  roleDescription: string;
  createdOn: string;
  createdBy: string;
  modifiedOn: string;
  modifiedBy: string;
  status: string;
}

export const ASSIGNMENT_ELEMENT_DATA: AssignmentElement[] = [
  { assignmentId: 1, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 2, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 3, action: 'Grant', userName: 'ADummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 4, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 8, action: 'Grant', userName: 'BDummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 1, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 12, action: 'Grant', userName: 'ZDummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 12, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 19, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 111, action: 'Grant', userName: 'PDummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 122, action: 'Grant', userName: 'Dummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { assignmentId: 133, action: 'Grant', userName: 'QDummy', roleName: 'admin', enabled: 'Y', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' }

];

export const ROLE_ELEMENT_DATA: RoleElement[] = [
  { roleId: 1, action: 'Create', roleName: 'admin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { roleId: 3, action: 'Create', roleName: 'cadmin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { roleId: 1, action: 'Create', roleName: 'admin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { roleId: 5, action: 'Create', roleName: 'vadmin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { roleId: 4, action: 'Create', roleName: 'zadmin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' },
  { roleId: 1, action: 'Create', roleName: 'admin', roleDescription: 'This is test role', createdOn: '21-Feb-2018', createdBy: 'Dummy', modifiedOn: '', modifiedBy: '', status: 'PENDING' }
];

@Component({
  selector: 'app-myqueue',
  templateUrl: './myqueue.component.html',
  styleUrls: ['./myqueue.component.css']
})
export class MyqueueComponent implements OnInit, AfterViewInit {

  dataSource1: MatTableDataSource<AssignmentElement>;
  dataSource2: MatTableDataSource<RoleElement>;
  @ViewChildren(MatPaginator) paginator = new QueryList<MatPaginator>();
  @ViewChildren(MatSort) sort = new QueryList<MatSort>();

  assignmentColumn: string[] = [
    'select', 'assignmentId', 'action', 'userName', 'roleName', 'enabled', 'createdOn', 'createdBy', 'modifiedOn', 'modifiedBy', 'status'
  ];
  roleColumn: string[] = [
    'select', 'roleId', 'action', 'roleName', 'roleDescription', 'createdOn', 'createdBy', 'modifiedOn', 'modifiedBy', 'status'
  ];

  constructor() {
    this.dataSource1 = new MatTableDataSource<AssignmentElement>(ASSIGNMENT_ELEMENT_DATA);
    this.dataSource2 = new MatTableDataSource<RoleElement>(ROLE_ELEMENT_DATA);
  }

  ngOnInit() {
  }

  ngAfterViewInit() {
    this.dataSource1.paginator = this.paginator.toArray()[0];
    this.dataSource1.sort = this.sort.toArray()[0];
    this.dataSource2.paginator = this.paginator.toArray()[1];
    this.dataSource2.sort = this.sort.toArray()[1];
  }

}
Manish Salian
  • 496
  • 5
  • 6
  • `this.paginator.toArray is not a function` it gives me this.paginator.toArray is not a function – Fares Ayyad Jun 01 '19 at 18:49
  • toArray() is still present in QueryList . Unless your variable(paginator) is not initalized to instance of QueryList you will get that error. https://angular.io/api/core/QueryList#toarray – Manish Salian Jun 04 '19 at 15:52
  • 1
    For those who want it short, @ViewChildren(MatPaginator) paginator = new QueryList(); this.dataSource1.paginator = this.paginator.toArray()[0]; this.dataSource2.paginator = this.paginator.toArray()[1]; – shaheer shukur Jul 22 '19 at 14:07
  • @ManishSalian - Your answer was very helpful. I finally got it to work with small addition. Unlike most of the examples or samples, my data sources for both the tables are dynamic. Data sources are NOT available during AfterViewInit. This caused the 2nd pagination not to work correctly. To correct the behavior, I initialized both the data sources with empty array and it worked. Example of empty array:dataSource: MatTableDataSource = new MatTableDataSource(); dataSourceDetails: MatTableDataSource = new MatTableDataSource(); – Prodip Sep 27 '20 at 18:38
  • @ManishSalian Problem with the above solution, ie, ``` this.dataSource1.paginator = this.paginator.toArray()[0]; this.dataSource1.sort = this.sort.toArray()[0];``` would only fix the pagination of a particular row, Is there a way that we can loop through the datasource and assign this.paginator.toArray()[index] to the datasource's paginator ? Here's what im trying to acheive https://stackoverflow.com/questions/66813576/mat-table-inside-a-mat-table-pagination-works-only-for-a-row-of-inner-mat-tabl – aki Mar 26 '21 at 12:04
  • Thank you so much for the detailed implementation. Extremely helpful. – TheNaturalTanuki Sep 14 '22 at 14:13
40

You can retrieve them by #id also:

In your HTML:

<mat-paginator #categoryPaginator [pageSizeOptions]="[15]" hidePageSize="true" showFirstLastButtons="false"></mat-paginator>

In your component TS:

@ViewChild('categoryPaginator', { read: MatPaginator }) categoryPaginator: MatPaginator;

EDIT

After seeing gh0st's comment I did some digging and it seems the "read" option is supposed to be a boolean ViewChild angular documentation so I did an experiment and { read: true } does not work - but {read: false} does - but given false is the default removing it altogether still works - so it seems this is the most succinct way to do this is:

@ViewChild('categoryPaginator') categoryPaginator: MatPaginator;

I have tested this with 3 tables/paginators in the one view component and they appear to still work as expected.

I can only assume that { read: MatPaginator } is actually being interpreted as { read: false }

The Angular documentation does actually give a rough example of this:

A template reference variable as a string (e.g. query <my-component #cmp></my-component> with @ViewChild('cmp'))

EDIT-2

I encountered a scenario where I have two material autocompletes in one component and need to access the MatAutoComplete trigger for one of them - it seems in this instance the { read: MatAutocompleteTrigger } is essential otherwise it just returns an ElementRef:

@ViewChild('localityAutoComplete', { read: MatAutocompleteTrigger }) trigger: MatAutocompleteTrigger;
Rob
  • 10,004
  • 5
  • 61
  • 91
  • 4
    This should be marked as the answer. Can you find documentation that supports this? – gh0st Jul 24 '19 at 15:34
  • 3
    @gh0st I've updated the answer to include references to the Angular docs and have also revised the solution as it's even simpler than I first thought. – Rob Jul 25 '19 at 01:10