28

I'm trying to filter a set of data by a specific object key Ex: I have a set of skills, I want to see all of the skills at level 2.

I have read through the docs, this GitHub example, and this other question but I can't find an actual example of how user input can be used to filter by an object key. So far nothing happens when a user clicks on a skill level.

Right now my HTML looks like:

<mat-button-toggle-group #group="matButtonToggleGroup"
    class="margin-1" (change)=toggleSkillLevel(group.value)>

  <mat-button-toggle value="0">
    0
  </mat-button-toggle>

  <mat-button-toggle value="1">
    1
  </mat-button-toggle>

  <mat-button-toggle value="2">
    2
  </mat-button-toggle>

  <mat-button-toggle value="all">
    ALL
  </mat-button-toggle>

</mat-button-toggle-group>

{{ dataSource.filteredData }}

and my TS looks like:

 import { Skill, SKILL_DATA } from '../data/skills-details';

...
...

  toggleSkillLevel(level){
    console.log('Only show level ' + level + ' skills...');
    this.dataSource.filterPredicate =
            (data: Skill, filter: string) => data.level == level;
  }
halfer
  • 19,824
  • 17
  • 99
  • 186
av0000
  • 1,917
  • 6
  • 31
  • 51
  • Does this answer your question? [custom filter in mat-table](https://stackoverflow.com/questions/48506606/custom-filter-in-mat-table) – luke77 Jun 17 '20 at 07:47

2 Answers2

54

By setting the filterPredicate, you only define how a filter value should be applied on your data when a filter value is given. It's only the definition of the filter function, you do not actually apply the filter with it. Hence, you only need to define this once, which could for example happen in the ngOnInit.

ngOnInit() {
  this.dataSource.filterPredicate =
      (data: Skill, filter: string) => !filter || data.level == filter;
}

To then apply your filter with an actual value, you need to set dataSource.filter, for example:

toggleSkillLevel(level) {
  this.dataSource.filter = level;
}
Kim Kern
  • 54,283
  • 17
  • 197
  • 195
  • 1
    This answer helped me fix this issue. Instead of `data.(myObj) == level` I put `data.(myObj) == filter` – jhhoff02 Sep 22 '18 at 16:49
  • Do you also know how to access `this` inside a filterPredicate? – AGoranov Jul 23 '19 at 09:22
  • @AGoranov Use an arrow function instead of an anonymous function as I did in the example. Then the `this` will be the same as in your component. – Kim Kern Jul 23 '19 at 09:24
  • @KimKern since my function is quite long it is done like this: `this.dataSource.filterPredicate = this.filterPredicate;`. The parameters are same as yours. – AGoranov Jul 23 '19 at 09:27
  • 2
    @AGoranov Do this instead: `this.dataSource.filterPredicate = (data: Skill, filter: string) => this.filterPredicate(data, filter);`. – Kim Kern Jul 23 '19 at 09:30
  • @KimKern Thank you for explaining this so concisely and correctly. It was exactly the kick-to-the-brain I needed to think of the predicate on a row-by-row basis and get it right. – AndrewBenjamin Dec 12 '19 at 21:51
0

Just wanted to add an addition to Kim Kern's answer.

If you apply this method and start getting an error like this:

TypeError: Cannot read property 'filter' of null
at MatTableDataSource._filterData (http://localhost.satin.lo:7000/vendor.js:88312:18)
at MapSubscriber.project (http://localhost.satin.lo:7000/vendor.js:88291:89)
at MapSubscriber._next (http://localhost.satin.lo:7000/vendor.js:141164:35)
at MapSubscriber.next (http://localhost.satin.lo:7000/vendor.js:137246:18)
at CombineLatestSubscriber.notifyNext (http://localhost.satin.lo:7000/vendor.js:137998:34)
at InnerSubscriber._next (http://localhost.satin.lo:7000/vendor.js:136528:21)
at InnerSubscriber.next (http://localhost.satin.lo:7000/vendor.js:137246:18)
at BehaviorSubject.next (http://localhost.satin.lo:7000/vendor.js:137030:25)
at BehaviorSubject.next (http://localhost.satin.lo:7000/vendor.js:136499:15)
at MatTableDataSource.set filter [as filter] (http://localhost.satin.lo:7000/vendor.js:88238:22)

This error is the result of your dataSource.data being null at the point in time that you call this.dataSource.filter = xyz;

I had something like this setting my dataSource from a parent components ajax call:

@Input() set items(data: IItem[]) {
     this.dataSource.data = data;
}

The default value from the observable was null. So I resolved this issue with a simple null check:

@Input() set items(data: IItem[]) {
    if(data){
        this.dataSource.data = data;
    }
}
Zze
  • 18,229
  • 13
  • 85
  • 118