Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Filters on MatTable

I've been trying to apply multi column filtering i.e a text input in column headers will filter only on the contents of the column.So far I've been able to make it work by overriding filterPredicate of MatTableDataSource but once I override the default filtering which is across columns no longer works.

export class TableFilteringExample implements OnInit
{
  displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
  dataSource = new MatTableDataSource(ELEMENT_DATA);

  positionFilter = new FormControl();
  nameFilter = new FormControl();

  filteredValues =
  {
    position: '',
    name: '',
    weight: '',
    symbol: ''
  };

  ngOnInit()
  {
    this.positionFilter.valueChanges.subscribe((positionFilterValue) =>
    {
      this.filteredValues['position'] = positionFilterValue;
      this.dataSource.filter = JSON.stringify(this.filteredValues);
    });

    this.nameFilter.valueChanges.subscribe((nameFilterValue) =>
    {
      this.filteredValues['name'] = nameFilterValue;
      this.dataSource.filter = JSON.stringify(this.filteredValues);
    });

    this.dataSource.filterPredicate = this.customFilterPredicate();
  }

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

  customFilterPredicate()
  {
    const myFilterPredicate = function(data: PeriodicElement, filter: string): boolean
    {
      let searchString = JSON.parse(filter);

      return data.position.toString().trim().indexOf(searchString.position) !== -1 && data.name.toString().trim().toLowerCase().indexOf(searchString.name)!== -1;
    }

    return myFilterPredicate;
  }
}

What I'm looking for is once column filter is applied the default filter should update the existing filter criteria and return the further filtered data.

StackBlitz

like image 547
kal93 Avatar asked Aug 24 '18 15:08

kal93


Video Answer


2 Answers

I think you just forgot to call toLowerCase() for searchString.name

data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase())!== -1;


EDIT: One approach is to create a global filter field in your Component class.

globalFilter = '';

<mat-form-field>
  <input matInput [ngModel]="globalFilter" (ngModelChange)="applyFilter($event)" placeholder="Filter">
</mat-form-field>

applyFilter(filter) {
    this.globalFilter = filter;
    this.dataSource.filter = JSON.stringify(this.filteredValues);
}

Then try to filter using global filter first before the other fields.

  customFilterPredicate() {
    const myFilterPredicate = (data: PeriodicElement, filter: string): boolean => {
      var globalMatch = !this.globalFilter;

      if (this.globalFilter) {
        // search all text fields
        globalMatch = data.name.toString().trim().toLowerCase().indexOf(this.globalFilter.toLowerCase()) !== -1;
      }

      if (!globalMatch) {
        return;
      }

      let searchString = JSON.parse(filter);
      return data.position.toString().trim().indexOf(searchString.position) !== -1 &&
        data.name.toString().trim().toLowerCase().indexOf(searchString.name.toLowerCase()) !== -1;
    }
    return myFilterPredicate;
  }

Here's the working app: https://stackblitz.com/edit/angular-hbakxo-5jeaic

like image 189
Randal Cunanan Avatar answered Oct 03 '22 14:10

Randal Cunanan


In Angular Material Tables, you can add multi colum filter using filterPredicate property on mat-tables and providing them a customFilter method as shown below

Source Link

Demo Link

enter image description here

        ngOnInit() {

        // Overrride default filter behaviour of Material Datatable
        this.dataSource.filterPredicate = this.createFilter();
        }

        ...
        ...

        // Custom filter method fot Angular Material Datatable
        createFilter() {
        let filterFunction = function (data: any, filter: string): boolean {
          let searchTerms = JSON.parse(filter);
          let isFilterSet = false;
          for (const col in searchTerms) {
            if (searchTerms[col].toString() !== '') {
              isFilterSet = true;
            } else {
              delete searchTerms[col];
            }
          }

          let nameSearch = () => {
            let found = false;
            if (isFilterSet) {
              for (const col in searchTerms) {
                searchTerms[col].trim().toLowerCase().split(' ').forEach(word => {
                  if (data[col].toString().toLowerCase().indexOf(word) != -1 && isFilterSet) {
                    found = true
                  }
                });
              }
              return found
            } else {
              return true;
            }
          }
          return nameSearch()
        }
        return filterFunction
        }
like image 34
Code Spy Avatar answered Oct 03 '22 14:10

Code Spy