Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

filterPredicate with empty filter string for Angular Material Table

I would like to make filterPredicate to work with empty filter string.

html:

<mat-form-field>
    <mat-label>Filter</mat-label>
    <input matInput (keyup)="applyFilter($event)" placeholder="Ex. ium" #input>
</mat-form-field>

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

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

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

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

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

        <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
        <tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.disabled == true"></tr>

        <!-- Row shown when there is no matching data. -->
        <tr class="mat-row" *matNoDataRow>
            <td class="mat-cell" colspan="4">No data matching the filter "{{input.value}}"</td>
        </tr>
    </table>

    <div [hidden]="dataSource.paginator == null">
        <mat-paginator pageSize="10" [pageSizeOptions]="[3, 5, 10]" showFirstLastButtons></mat-paginator>
    </div>

ts:

import { Component, OnInit, AfterViewInit, ViewChild } from "@angular/core";
import { MatTableDataSource } from "@angular/material/table";
import { MatPaginator } from "@angular/material/paginator";

export interface PeriodicElement {
  name: string;
  position: number;
  weight: number;
  symbol: string;
  disabled: boolean;
}

const ELEMENT_DATA: PeriodicElement[] = [
  {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H', disabled: false},
  {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He', disabled: false},
  {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li', disabled: false},
  {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be', disabled: false},
  {position: 5, name: 'Boron', weight: 10.811, symbol: 'B', disabled: false},
  {position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C', disabled: false},
  {position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N', disabled: false},
  {position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O', disabled: true},  // should be hidden.
  {position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F', disabled: true}, // should be hidden.
  {position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne', disabled: true}, // should be hidden.

];

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit, AfterViewInit {
  displayedColumns: string[] = ["position", "name", "weight", "symbol"];
  dataSource = new MatTableDataSource(ELEMENT_DATA);
  @ViewChild(MatPaginator) paginator!: MatPaginator;

  ngOnInit(): void {

    // This does not run in case filter is empty.
    this.dataSource.filterPredicate = (
      data: PeriodicElement,
      filter: string
    ) => {
      if (data.disabled) {
        return false;
      }

      if (data.name.toLowerCase().indexOf(filter) > -1) {
        return true;
      }
      return false;
    };
  }

  ngAfterViewInit(): void {
    this.dataSource.paginator = this.paginator;
  }

  applyFilter(event: Event) {
    const filterValue = (event.target as HTMLInputElement).value;
    this.dataSource.filter = filterValue.trim().toLowerCase();
  }
}

I would like to hide rows which have disabled:true (position 8, 9, 10 in the example above).

In my previous post, I first tried to set filterPredicate. But this does not work in case filter string is empty. So I set [hidden] parameter in html as below.

<tr mat-row *matRowDef="let row; columns: displayedColumns;" [hidden]="row.disabled == true"></tr>

This looks fine, but I have a problem.

When the table is shown with paginator, the paginator's row count does not match dispalyed rows count.

enter image description here

This is Stackblitz sample. 7 rows are shown, but paginator's row count are 10.

I guess everything will be good if filterPredicate can be applied with empty filter string.

Do I have a way for filterPredicate to work even for empty string? Or do I have another solutions for this problem?

like image 711
N.F. Avatar asked Sep 15 '25 16:09

N.F.


1 Answers

I found a strange solution to this.

Knowing that datasource.filterPredicate is triggered only when dataSource.filter is not empty set the dataSource.filter to any string, say 'dummy' and then inside the datasource.filterPredicate implement any logic.

let filterValue: String   
ngOnInit(): void {
        // This does not run in case filter is empty.
            this.dataSource.filterPredicate = (
              data: PeriodicElement
            ) => {
              if (data.disabled) {
                return false;
              }
        
              if (data.name.toLowerCase().indexOf(this.filterValue) > -1) {
                return true;
              }
              return false;
            };
          }
        
          ngAfterViewInit(): void {
            this.dataSource.paginator = this.paginator;
          }
        
          applyFilter(event: Event) {
            this.dataSource.filter = this.filterValue || "dummy";
          }
    }

letting filterValue bind to your textelement

    <input matInput [(ngModel)]="filterValue" (keyup)="applyFilter($event)" placeholder="Ex. ium" #input>

inside the applyFilter doesn't mind at all the filter parameter, so some code is unnecessary

like image 155
user2252941 Avatar answered Sep 17 '25 05:09

user2252941