Search code examples
angularangular-materialangular-material-table

Angular Paginator after filtering


I want to implement to my project, sorting, paging and filtering.When I go to filter the data in the table it shows me only the correspondence of the data present on the current page.Let's say there are only 5 data on the first page, the filter works only on these. It does not work on the entire page.

Here is codes

ts:

export class ListComponent extends BaseComponent implements OnInit {
  constructor(spinner: NgxSpinnerService,
    private productService: ProductService,
    private alertifyService: AlertifyService,
    private dialogService: DialogService) {
    super(spinner)
  }


  displayedColumns: string[] = ['name', 'stock', 'price', 'createdDate', 'updatedDate', 'photos', 'qrcode', 'edit', 'delete'];
  dataSource: MatTableDataSource<List_Product> = null;
  @ViewChild(MatPaginator) paginator: MatPaginator;
  @ViewChild(MatSort) sort: MatSort;

  async getProducts() {
    this.showSpinner(SpinnerType.BallAtom);
    const allProducts: { totalProductCount: number; products: List_Product[] } = await this.productService.read(this.paginator ? this.paginator.pageIndex : 0, this.paginator ? this.paginator.pageSize : 5, () => this.hideSpinner(SpinnerType.BallAtom), errorMessage => this.alertifyService.message(errorMessage, {
      dismissOthers: true,
      messageType: MessageType.Error,
      position: Position.TopRight
    }))
    this.dataSource = new MatTableDataSource<List_Product>(allProducts.products);
    this.paginator.length = allProducts.totalProductCount;
  }

  addProductImages(id: string) {
    this.dialogService.openDialog({
      componentType: SelectProductImageDialogComponent,
      data: id,
      options: {
        width: "1400px"
      }
    });
  }

  async pageChanged() {
    await this.getProducts();
  }

  async ngOnInit() {
    await this.getProducts();
  }

  showQRCode(productId: string) {
    this.dialogService.openDialog({
      componentType: QrcodeDialogComponent,
      data: productId,
      afterClosed: () => { }
    })
  }

  ngAfterViewInit() {
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
  }


  filter(filterValue: string) {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

}

html

<div class="example-header">
  <mat-form-field>
    <input matInput (keyup)="filter($any($event.target).value)" placeholder="Filter">
  </mat-form-field>
</div>
<div class=" mat-elevation-z8">
  <table mat-table [dataSource]="dataSource" matSort>

    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
      <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container>

    <ng-container matColumnDef="stock">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Stock </th>
      <td mat-cell *matCellDef="let element"> {{element.stock}} </td>
    </ng-container>

    <ng-container matColumnDef="price">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Price </th>
      <td mat-cell *matCellDef="let element"> {{element.price}}$ </td>
    </ng-container>

    <ng-container matColumnDef="createdDate">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Created Date </th>
      <td mat-cell *matCellDef="let element"> {{element.createdDate}} </td>
    </ng-container>

    <ng-container matColumnDef="updatedDate">
      <th mat-header-cell *matHeaderCellDef mat-sort-header> Updated Date </th>
      <td mat-cell *matCellDef="let element"> {{element.updatedDate}} </td>
    </ng-container>

    <ng-container matColumnDef="photos">
      <th mat-header-cell *matHeaderCellDef width="30">  </th>
      <td mat-cell *matCellDef="let element"> <img style="cursor:pointer" (click)="addProductImages(element.id)" src="../../../../../assets/add_a_photo_24dp_5F6368_FILL0_wght400_GRAD0_opsz24.png" width="25" height="25" /> </td>
    </ng-container>

    <ng-container matColumnDef="qrcode">
      <th mat-header-cell *matHeaderCellDef width="30">  </th>
      <td mat-cell *matCellDef="let element"> <img style="cursor:pointer" src="../../../../../assets/qrcode.png" width="25" height="25" (click)="showQRCode(element.id)" /> </td>
    </ng-container>

    <ng-container matColumnDef="edit">
      <th mat-header-cell *matHeaderCellDef width="30">  </th>
      <td mat-cell *matCellDef="let element"> <img style="cursor:pointer;" src="../../../../../assets/update_24dp_5F6368_FILL0_wght400_GRAD0_opsz24.png" width="25" height="25" /> </td>
    </ng-container>

    <ng-container matColumnDef="delete">
      <th mat-header-cell *matHeaderCellDef width="30">  </th>
      <td mat-cell *matCellDef="let element"
          appDelete
          [id]="element.id"
          (callback)="getProducts()"
          controller="products">
      </td>
    </ng-container>

    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

  </table>

  <mat-paginator (page)="pageChanged()"
                 [pageSizeOptions]="[5, 10, 20, 50, 100]"
                 showFirstLastButtons
                 aria-label="Select page of periodic elements">
  </mat-paginator>
</div>

Solution

  • You are applying filtering incorrectly, you need to send the filtering information to the backend as a query parameter. Then filter the data on the backend and return the first page, etc.

    Would recommend you perform the above changes, if it's not necessary, then go for the below approach.


    Currently, the problem with your code is that, you are resetting the filter through the below line:

    this.dataSource = new MatTableDataSource<List_Product>(allProducts.products);
    

    We can reapply the filter after recreating the datasource, we create the clone using structuredClone:

    async getProducts() {
        this.showSpinner(SpinnerType.BallAtom);
        const allProducts: { totalProductCount: number; products: List_Product[] } = await this.productService.read(this.paginator ? this.paginator.pageIndex : 0, this.paginator ? this.paginator.pageSize : 5, () => this.hideSpinner(SpinnerType.BallAtom), errorMessage => this.alertifyService.message(errorMessage, {
          dismissOthers: true,
          messageType: MessageType.Error,
          position: Position.TopRight
        }))
        // take a backup of the filter as a clone.
        const filterClone = structuredClone(this.dataSource.filter);
        const sortClone = structuredClone(this.dataSource.sort);
        this.dataSource = new MatTableDataSource<List_Product>(allProducts.products);
        if(filterClone) {
            this.dataSource.filter = filterClone;
        }
        if(sortClone) {
            this.dataSource.sort = sortClone;
        }
        this.dataSource.paginator = this.paginator;
        this.paginator.length = allProducts.totalProductCount;
    }