Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Abhijith-Nagaraja opened this issue Mar 1, 2018 · 22 comments
Assignees

Comments

@Abhijith-Nagaraja
Copy link

Abhijith-Nagaraja commented Mar 1, 2018

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

@Abhijith-Nagaraja
Copy link
Author

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('');
    }
  }

@andrewseguin
Copy link
Contributor

Unfortunately this is an Angular-thing where ViewChild doesn't catch elements with *ngIf in ngAfterViewInit. Your workaround looks feasible to me

@rain01
Copy link

rain01 commented Mar 8, 2018

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.

@Abhijith-Nagaraja
Copy link
Author

Abhijith-Nagaraja commented Mar 9, 2018

@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('');
    }

@mdhvfrnd
Copy link

Hi when we put the condition still this is not working.

<mat-paginator *ngIf="allItems ?.length > 0" #paginator [pageSize]="50" [pageSizeOptions]="[1,2,4,20, 40,100,150,200]">

@Sanafan
Copy link

Sanafan commented May 13, 2018

the amount found items gets displayed correctly but pagination is still not working correctly :( how do i add another paginator and sort to your example @Abhijith-Nagaraja ? I have two tables in one component and cant get both paginators and sortings to work

@bresleveloper
Copy link

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"

@lpalbou
Copy link

lpalbou commented Jun 19, 2018

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

@kerby82
Copy link

kerby82 commented Jun 20, 2018

Same here the *ngIF break the paginator. I guess this is a bug.

@KumarSidharth
Copy link

KumarSidharth commented Jun 29, 2018

As suggested by @lpalbou *ngIf breaks mat-paginator and mat-sort.
The reason is that the value of paginator/sort in your ts file is not instantiated until the component renders it on your DOM.
Therefore you need to make sure that your DataSource object is generated after paginator renders on the DOM.
There are numerous ways to handle the issue.

  • [hidden] as suggested is one.

  • You may be getting data from an API call. You may initially show the table and hide it once you make an API call. After you get the data from API call show the table again.

  • Giving the opacity of the table as 0 initially. Turning opacity to 1 after you get the Data. You can add CSS transitions to it.

  • Giving the table width and height to 0 initially. Changing width and height to the desired value with a CSS transition.

You can take the code for CSS transitions from animista.com

@mostafamahdijoo
Copy link

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

@dheerpandey
Copy link

Thanks @Abhijith-Nagaraja your solution worked for me.

@TimoPot
Copy link

TimoPot commented Jul 18, 2018

Please document the issue and solution on https://material.angular.io/components/paginator/api

chrisekelley added a commit to Tangerine-Community/Tangerine that referenced this issue Jul 31, 2018
hide its div to avoid undefined this.paginator as per angular/components#10205 (comment)
@csiddu14
Copy link

csiddu14 commented Dec 5, 2018

If multiple pagination are used in same page then we should use @ViewChildren

Example:
@ViewChildren(MatPaginator) paginator = new QueryList();

table1:
this.followUpListData.paginator=this.paginator.toArray()[0];

table2:
this.activeListData.paginator=this.paginator.toArray()[1];

@Davel-11
Copy link

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!!!

@Shyam-Chen
Copy link

Shyam-Chen commented Feb 20, 2019

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

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

@sinswa28
Copy link

I had also faced same issue with Paginator and sorting with *ngIf . [hidden] attribute solved my issue too. Thank you so much @lpalbou !! :)

@brandontg
Copy link

I tried applying the [hidden] attribute to a mat-card element wrapped around the mat-table, but that did not work; however, wrapping the card in a div and adding the [hidden] attribute to that did work.

@ThomasAdr
Copy link

-  @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.

@cisco336
Copy link

Hi guys, I just got mine working by making this:
Change: @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
to: @ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;

and then after I get the data from subscribe or promise:
this.dataSource.paginator = this.paginator;

@mattiLeBlanc
Copy link

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.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests