Multiple material pagination in one component doesn't work in Angular
M

3

26

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?

Minnich answered 19/5, 2018 at 19:9 Comment(3)
Do you need to use @ViewChildren in this case?Umeh
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.Minnich
If you have more than one MatPaginator on the page, then @ViewChildren(MatPaginator) should give you QueryList<MatPaginator>, according to the docs. What you do with that list depends on what you are trying to do in the end.Umeh
M
48

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

}
Merriott answered 30/6, 2018 at 16:13 Comment(6)
this.paginator.toArray is not a function it gives me this.paginator.toArray is not a functionThreeply
toArray() is still present in QueryList . Unless your variable(paginator) is not initalized to instance of QueryList you will get that error. angular.io/api/core/QueryList#toarrayMerriott
For those who want it short, @ViewChildren(MatPaginator) paginator = new QueryList<MatPaginator>(); this.dataSource1.paginator = this.paginator.toArray()[0]; this.dataSource2.paginator = this.paginator.toArray()[1];Disbelieve
@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<ARResult> = new MatTableDataSource<ARResult>(); dataSourceDetails: MatTableDataSource<HttpLog> = new MatTableDataSource<HttpLog>();Supercolumnar
@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 #66814076Liminal
Thank you so much for the detailed implementation. Extremely helpful.Glyptograph
P
42

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;
Perception answered 8/7, 2019 at 8:17 Comment(2)
This should be marked as the answer. Can you find documentation that supports this?Stocky
@Stocky 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.Perception
D
0

Be careful when using ViewChild with an ngIf directive whose value is false, as the ViewChild will not capture any value.

Diwan answered 4/4 at 14:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.