skip to Main Content

Using any kind of event I managed to find from the API for select and autocomplete ends up with the MatPaginator being ‘a step behind’, e.g. while the filtered data is supposed to be only 200 in length, the paginator.length is still stuck at 800, and only becomes 200 after the next selection.

.html

<mat-form-field class="filterField">
  <input matInput
         (keyup)="updateManualPage(1)"
         placeholder="Filter"
         formControlName="filterParam2"
         [matAutocomplete]="autoSingleSelect">
  <mat-autocomplete #autoSingleSelect="matAutocomplete"
                    class="filterSelect"
                    panelClass="filterSelect">
    <mat-option *ngFor="let option of dropdownSingleFilteredOptions | async"
                [value]="option.param2">
      {{option.param2}}
    </mat-option>
  </mat-autocomplete>
</mat-form-field>

<div id="paginationContainer">
  <div id="pageNumberButtons">
    <button mat-button
        [class.hiddenPage]="!(manualPage > 3)"
        (click)="updateManualPage(1)">
      1
    </button>
...etc
  </div>
  <mat-paginator [length]="tableDataSource.data.length"
                 [pageSize]="10"
                 [pageSizeOptions]="[5, 10, 25]"
                 showFirstLastButtons
                 aria-label="Select page"
                 [selectConfig]="{ panelClass: 'paginatorDropdown' }"
                 (page)="onPaginateChange($event)">
  </mat-paginator>
</div>

.ts

tableDataSource = new MatTableDataSource<randomInterface>(testArray);
maxPages: number = 0;
manualPages: number = 1;

ngOnInit() {
  this.tableDataSource.filterPredicate = this.customFilter;
}

ngAfterViewInit() {
  this.updatePaginator();
  this.updateMaxPages();
}

onPaginateChange(event: PageEvent) {
  this.manualPage = event.pageIndex + 1;
  this.updatePaginator();
  this.updateMaxPages();
}

updateManualPage(page: number) {
  this.manualPage = page;
  this.tableDataSource.paginator!.pageIndex = page - 1;
  this.updatePaginator();
  this.updateMaxPages();
}

updatePaginator() {
  this.tableDataSource.paginator = this.paginator;
}

updateMaxPages() {
  this.maxPages = Math.ceil((this.tableDataSource.paginator?.length ?? 0) / (this.tableDataSource.paginator?.pageSize ?? 1));
}

customFilter = (data: randomInterface, filter: string) => {
  const filterData = JSON.parse(filter);

  let find: boolean = !filterData.filterParam1 || data.param1.toString().includes(filterData.filterParam1.toString());
  find = find && (!filterData.filterParam2 || data.param2.toLowerCase().includes(filterData.filterParam2.toLowerCase()));
  find = find && (!filterData.filterParam3 || filterData.filterParam3 == '' ? true : filterData.filterParam3.some((filterValue: string) => filterValue.toLowerCase() == data.param3.toLowerCase()));

  return find;
}

I’ve tried updating this.maxPages at multiple different points in the .ts code, but no matter where I put it and which event I use on the HTML side, the paginator itself is always lagging behind, and I’m personally unsure of how to use, and if it would even help, to use ._updatePaginator(filteredDataLength: number) since I am using a predicate to filter the data. It used to not work for the elements either, at least with (keydown) and (input) events since they, for the lack of a better word, ‘too fast’ for the paginator, while (keyup) worked perfectly.

EDIT: After doing some exploring it seems that while the this.tableDataSource.Paginator is updating, and comes before the call for updateManualPage(1) on the autocomplete event, for some reason this.maxPages still stays the same.

Doing

updateMaxPages() {
  console.log(this.tableDataSource);
  this.maxPages = Math.ceil((this.tableDataSource.paginator?.length ?? 0) / (this.tableDataSource.paginator?.pageSize ?? 1));
}

shows that the paginator is indeed updating, the literal next line is for some reason a whole call behind.

2

Answers


  1. Chosen as BEST ANSWER

    Setting a timeout of 0

    updateMaxPages() {
      setTimeout(() => {
        this.maxPages = Math.ceil((this.tableDataSource.paginator?.length ?? 0) / (this.tableDataSource.paginator?.pageSize ?? 1));
      })
    }
    

    has 'fixed' it but for some reason I feel like this is going to create a lot of problems in the future. Any input is welcome!


  2. If you use a mat-table and you’re not using an API to filter, you needn’t make anything with the paginator (material makes the work for you). See the example "Data table with sorting, pagination, and filtering." in the docs

    BTW when yo do

    this.dataSource.filter = ...
    

    use

    this.dataSource.filter = JSON.stringify(){
       param1:form.get('filterParam1').value.toString()
       param2:form.get('filterParam2').value.toLowerCase()
       param2:form.get('filterParam3').value.toLowerCase()
    }
    

    In this way you don’t need makes each time the toLowerCase…

    NOTE: The setTimeout without miliseconds force to Angular to repaint. It’s necessary because this.tableDataSource.paginator?.length has no the correct value when you executed directly and is perfectly valid (well, really not because it’s not necessary recalculate manually.)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search