components: Mat Paginator is not working properly along when used conditional rendering (*ngIf) on the outer div.

Bug, feature request, or proposal:

Bug

What is the expected behavior?

When conditional rendering is done using *ngIf on table on outer <div>, Mat-Paginator should paginate properly

What is the current behavior?

If table is inside a conditional *ngIf and is hidden to start with, then after the condition is satisfied and view is initialized, data is not paginated. And the paginator is displayed as below screen shot 2018-02-28 at 7 43 58 pm

What are the steps to reproduce?

With conditional rendering where we can see the problem clearly https://stackblitz.com/edit/angular-material2-issue-s1hkz9?file=app/app.component.html

Without conditional rendering, it works properly https://stackblitz.com/edit/angular-material2-issue-3xb5aa?file=app/app.component.html

What is the use-case or motivation for changing an existing behavior?

We need to display a table with pagination only if certain condition is met

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

"@angular/animations": "^5.2.0",
"@angular/cdk": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/material": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
"typescript": "~2.5.3"

Is there anything else we should know?

NA

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 36
  • Comments: 22 (1 by maintainers)

Commits related to this issue

Most upvoted comments

I have the solution. But I don’t know whether this is the correct way or a workaround. Please feel free to close this issue, if this is how it is designed

This is also applicable to MatSort.

Remove the following piece of code from ngAfterViewInit

@ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }

Add the following piece of code

  private paginator: MatPaginator;
  private sort: MatSort;

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

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

  setDataSourceAttributes() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;

    if (this.paginator && this.sort) {
      this.applyFilter('');
    }
  }

Yes the *ngIf breaks the Material Paginator, however, instead of using *ngIf, you could use the [hidden] attribute like this:

<div [hidden]="isLoading">
  <mat-table [dataSource]="dataSource">
  ...
  </mat-table>
</div>

I have tested it on Angular 5 and it does work

Yes the *ngIf breaks the Material Paginator, however, instead of using *ngIf, you could use the [hidden] attribute like this:

<div [hidden]="isLoading">
  <mat-table [dataSource]="dataSource">
  ...
  </mat-table>
</div>

I have tested it on Angular 6 and it does work, THANK YOUUUU!!!

@rain01: applyFilter() method is a custom method. This is typically used to filter the datagrid/table.

Here is the sample for that

applyFilter(filterValue: string) {
        filterValue = filterValue.trim(); // Remove whitespace
        filterValue = filterValue.toLowerCase(); // Datasource defaults to lowercase matches
        this.dataSource.filter = filterValue;
    }

This will still work if you remove the following

if (this.paginator && this.sort) {
      this.applyFilter('');
    }
-  @ViewChild(MatPaginator) paginator: MatPaginator;
- 
-  ngOnInit() {
-    this.dataSource.paginator = this.paginator;
-  }

+  @ViewChild(MatPaginator) set matPaginator(paginator: MatPaginator) {
+    this.dataSource.paginator = paginator;
+  }

@ViewChild(MatPaginator, {static: true}) set matPaginator(paginator: MatPaginator) { this.dataSource.paginator = paginator; }

A quick and easy way to fix the issue, idk why this answer is not among the most upvoted.

I just wanted to make the paginator optional in my Table component, so I went for the SET solution.

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

    <!-- Position Column -->
    <ng-container matColumnDef="position">
      <th mat-header-cell *matHeaderCellDef> No. </th>
      <td mat-cell *matCellDef="let element"> {{element.position}} </td>
    </ng-container>
    ......

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  </table>
  <mat-paginator [pageSizeOptions]="[5, 10, 20]" showFirstLastButtons *ngIf="pagination"></mat-paginator>
</div>
export class TableComponent implements OnInit {
  @Input('pagination') pagination: boolean;
  @ViewChild(MatPaginator,  {static: false}) set matPaginator(paginator: MatPaginator) {
    if (this.pagination) {
      this.dataSource.paginator = paginator;
    }
  }
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource<PeriodicElement>(ELEMENT_DATA);
  ....
}

Seems to me the issue happens when Angular initialises the datasource before the template has been rendered. So when the Mat-pagination is missing, it doesn’t seem to initialise the datasource properly. The Set Fix in the ViewChild as proposed by people above works for now.

-  @ViewChild(MatPaginator) paginator: MatPaginator;
- 
-  ngOnInit() {
-    this.dataSource.paginator = this.paginator;
-  }

+  @ViewChild(MatPaginator) set matPaginator(paginator: MatPaginator) {
+    this.dataSource.paginator = paginator;
+  }

I had the same issue, and using “[hidden]” attribute as @lpalbou said, solved my problem. thanks @lpalbou .

constructor(private s:StubService) {
    console.log('welcome to TableComponent')
    this.dataSource = new MatTableDataSource([]);

    this.s.data.subscribe(allitems => {
      console.log('TableComponent data subscribe')
      this.dataSource = new MatTableDataSource(allitems);
      this.dataSource.paginator = this.paginator;
      this.dataSource.sort = this.sort;
    })
  }

notice the new MatTableDataSource([]);, that was my solution instead of *ngIf="dataSource"

I have the same issue but I’m trying to load the data from api. @Abhijith-Nagaraja What is applyFilter()? It says it doesn’t exist in my code.