Search code examples
angulartypescriptmat-table

How to filter a MatTable in angular without filterPredicate


I have MatTable that i want to filter using values i store from the user input I am able to check that that the value match the data but I don't know how to filter the table without using filterPredicare as using another one will get me a duplication problem.

This is my component NgOnInit :

 ngOnInit() {
    this.marinService.getAllContainers().subscribe((result) => {
     //Data
      this.dataSource = new MatTableDataSource(result);
      //Paginator
      this.dataSource.paginator = this.paginator;
      //AutoFilter Form 1st page
      this.clientType = this.route.snapshot.queryParamMap.get('clientType');
      this.storageType= this.route.snapshot.queryParamMap.get('storageTypes');
      console.log('The Client name is : '+this.clientType+'  '+'The storage Facility is : '+this.storageType);
      this.tableFilter();
      //snapShot Filter
      //CheckBoxFilter
      this.dataSource.filterPredicate = (data: Container, filter: any) => {
        return filter.split(',').every((item: any) => data.SOG_MCOLH.indexOf(item)!== -1);
      };
      this.filterCheckboxes.subscribe((newFilterValue: any[]) => {
        this.dataSource.filter = newFilterValue.join(',');
      });
      });
  }

This is my function :

  tableFilter(){
    var data : Container = this.dataSource;
    var newData: any[];
    if(data.LQOCH_SHM_LOEZI_QTSR = this.clientType){
      console.log(data.LQOCH_SHM_LOEZI_QTSR);
    }else{
      console.log("Hi Not working")
      return this.dataSource;
    }
  }

I have tried using the same "filtering method" as i used in the filterPrediacte and it didn't work out.


Solution

  • In my opinion, it would be better to modify the filterPredicate() to suit your needs. The mat-table has been optimized to improve performance so creating your own filter for the table without the use of filterPredicate() could result in performance issues. I have a working example of this on stackblitz.

    In my example, I am creating a filter as an object of the below format

    {
      columnName: Array(filterValues),
      ...
    }
    

    (since in a checkbox, you can filter through particular columns based on a particular value and there can be multiple filters on the same column)

    I am using a function updateFilter() to modify the filter based on the selected checkbox. This will just add the filter to the filter object if the checkbox is checked and remove it from the filter object once unchecked. This filter object will then be passed to the mat-table filter as a string (since the filterPredicate() only accepts string values)

    updateFilter(column: string, filter: string) {
      if (!this.filterValues.hasOwnProperty(column)) {
        this.filterValues[column] = [filter];
      } else {
        this.filterValues[column].includes(filter) ?
        this.filterValues[column] = this.filterValues[column].filter(filterValue => filterValue !== filter) :
        this.filterValues[column].push(filter);
      }
    
      this.applyFilter();
    }
    

    In filterPredicate(), parse the filter object and filter the data based on this object. I am using the variable conditions since there can be multiple checkboxes selected at once (we will need to pass all data that satisfies all selected conditions. try selecting all checkboxes in the example). For any custom filter (eg. show all data with progress > 90%), you can add the condition. I have added a custom filter for name since the column contains both first name and last name while I am only filtering based on first name (subset of the name).

    this.dataSource.filterPredicate = ((data: UserData, filter: string): boolean => {
      const filterValues = JSON.parse(filter);
      let conditions = true;
    
      for (let filterKey in filterValues) {
        if (filterKey === 'name') {
          conditions = conditions && data[filterKey].trim().toLowerCase().indexOf(filterValues[filterKey]) !== -1;
        }
        else if (filterValues[filterKey].length) {
          conditions = conditions && filterValues[filterKey].includes(data[filterKey].trim().toLowerCase());
        }
      }
    
      return conditions;
    });
    

    Finally, in order to trigger the filter. On selecting the checkbox, just call updateFilter() along with the columnName to filter through and the value. For eg. to show red colors, call updateFilter('color', 'red') where 'color' is the column name and 'red' is the filter value.

    Edit: There are different ways you can do what you mentioned in the comments. You could set the value to your checkbox and add a template reference variable. Below #color is your template reference variable. This allows you to access this checkbox value in your html by using color.value

    <mat-checkbox value="blue" (change)="updateFilter('color', color.value)" #color>Show blue colors</mat-checkbox>
    

    If you have the value defined in your component and you are passing it to the checkbox using [value]. You can either use a template reference variable again to pass the data or just pass the value itself.

    component

    name: string = 'Amelia';
    

    HTML

    // No interpolation required when passing the value to the function.
    <mat-checkbox [value]="name" (change)="updateFilter('name', name)">Show all first name - Amelia</mat-checkbox>
    

    Check out the updated stackblitz to see this in action.