custom filter in mat-table

85,000

Solution 1

I found the solution here.

It's necessary to rewrite filterPredicate, and just use it as usual, filterPredicate needs to return true when filter passes and false when it doesn't

export interface Element {
 name: string;
 position: number;
 weight: number;
 symbol: string;
}


dataSource = new MatTableDataSource(ELEMENT_DATA);
/* configure filter */
this.dataSource.filterPredicate = 
  (data: Element, filter: string) => data.name.indexOf(filter) != -1;


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

Solution 2

Don't forget to apply .trim().toLowerCase() on your data or you may encounter unexpected results. See my example below:

this.dataSource.filterPredicate = (data:
  {name: string}, filterValue: string) =>
  data.name.trim().toLowerCase().indexOf(filterValue) !== -1;

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

Solution 3

Be aware that all fields of the class displayed in table rows, are subject to filtering, even if you do not display that field as a column.

export class City {
  id: number;//is not displayed in mat table
  code: string;
  name: string;
  country:Country;
}

Any filter for the dataSource of city table, also applies to id column, which generally we do not display to the end user.

//this filter will also apply to id column
this.cityDataSource.filter = filterValue;

Solution 4

when using parent-child componenet (or service with say observable) you must always in the component containing the MatTableDataSource set an "applyFilter" function (name not matters)

in the table component i added @Input's for the filter string value as a FormControl, and sent a filter function (filterfn)

  @Input() data: any[];
  @Input() filterControl: FormControl;
  @Input() filterfn: (data: any, filter: string) => boolean;

in the init

 ngOnInit(): void {
    this.dataSource.filterPredicate = this.filterfn
    this.dataSource.data = this.data;
    this.initExpandedDefaultValues();
    //my "applyFilter"
    this.filterControl.valueChanges.subscribe(searchValue => this.dataSource.filter = searchValue)
  }

in parent html

<div *ngIf="users">
    <expended-table [data]="users"
        [filterfn]="filterFunction"
        [filterControl]="search">
    </expended-table>
</div>

in parent component

public users:User[]
  search:FormControl = new FormControl()
  public usersFiltered:User[]

  filterFunction(u: User, searchValue: string) : boolean{
    if (searchValue) {
      let v = searchValue.trim().toLowerCase()
      if (
        u.firstName.toLowerCase().includes(v) || 
        u.lastName.toLowerCase().includes(v) || 
        //bla bla bla more tests
        u.permission.toLowerCase().includes(v)  
      )
      { return true } 
      else { return false}
    } //end if searchValue
    else 
    {
      return true
    }
  }
Share:
85,000
cucuru
Author by

cucuru

Updated on November 12, 2020

Comments

  • cucuru
    cucuru over 3 years

    I'm using mat-table. It has a filter which works fine with doc example:

    From https://material.angular.io/components/table/overview, the original code is:

        <div class="example-header">
           <mat-form-field>
             <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter">
           </mat-form-field>
       </div>
    
       <mat-table #table [dataSource]="dataSource">
          <!-- the rest of the code -->
       </mat-table>
    
        export class TableFilteringExample {
         displayedColumns = ['position', 'name', 'weight', 'symbol'];
         dataSource = new MatTableDataSource(ELEMENT_DATA);
    
         applyFilter(filterValue: string) {
           filterValue = filterValue.trim(); // Remove whitespace
           filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
           this.dataSource.filter = filterValue;
         }
        }
        const ELEMENT_DATA: Element[] = [
         {position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
         {position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
         {position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
         {position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
         {position: 5, name: 'Boron', weight: 10.811, symbol: 'B'}
        ]; 
    

    With this implementation, when filter, it filter for any column.

    Now I'm trying to change the filter because I want is filter just for "name" column, so I'm trying to rewrite the filter and assign to filterData.

          applyFilter(filterValue: string) {
            filterValue = filterValue.trim(); // Remove whitespace
            filterValue = filterValue.toLowerCase(); // MatTableDataSource defaults to lowercase matches
           this.dataSource.filteredData = this.filterByEmail(filterValue); 
            console.log(this.dataSource.filteredData); //value is what I want.
        }
    
        filterByName(filter: string): any {
          const dataFiltered = this.data.filter(function(item){
             return item.name.indexOf(filter) > -1
           })
            return dataFiltered;
        }
    

    In console, I can see this.dataSource.filteredData has the data I want to print, but table is not reload.

    What are I'm missing?

  • munificent
    munificent over 3 years
    adding this.dataSource.filterPredicate = (data: Element, filter: string) => data.name.indexOf(filter) != -1; in ngOnInit() method and the IDE shows this: TS2322: Type '(data: Element, filter: string) => boolean' is not assignable to type '(data: string, filter: string) => boolean'. Types of parameters 'data' and 'data' are incompatible. Type 'string' is not assignable to type 'Element'. anyone know why?
  • Amit Kumawat
    Amit Kumawat almost 3 years
    how to avoid id from the filter?
  • Selman Gun
    Selman Gun almost 3 years
    filterPredicate of data source may be used as mentioned below.