Reputation: 547
Hi have a Data table with 4 cols- Name, Age, Marks, School. Now I want to apply filter for each of the columns. The filter works fine individually. But I am not able to combine the results of filter - example I am not able to filter out the Name which has marks > 90 and from 'abc' School.
I tried something like this, but it did not work.
applyFilter(filterValue: string) {
console.log("Filter is applied upon:",this.dataSource)
let initialDataSource = this.dataSource;
this.dataSource.filter = filterValue.trim().toLowerCase();
this.filteredDataSource = this.dataSource.filteredData
this.filterVal = this.dataSource.filter;
if(this.filterVal.length != 0){
this.dataSource = new MatTableDataSource(this.filteredDataSource);
this.dataSource.filter = filterValue.trim().toLowerCase();
}else{
this.dataSource = initialDataSource;
this.dataSource.filter = filterValue.trim().toLowerCase();
this.filteredDataSource = this.dataSource.filteredData
}
}
The above function does the filtering, but when I clear out the filter value, the table data does not change. Also it does not change when I delete a character.
Upvotes: 7
Views: 15825
Reputation: 459
Althoug the answer from grreeenn is perfectly fine, I wasn't able to combine multiple filter values with this approach. This is what I came up with:
The filters array would consist of multiple objects
const filters = [{key: name, filterValue: 'Mark'}, {key: score, filterValue: 1}]
To make the filterPredicate accept the list you can stringify the array with
JSON.stringify(filters)
I changed the filterPredicate method a little to work with multiple values at once
customFilterPredicate(data: any, filters: string): boolean {
let match = true;
const filtersList = JSON.parse(filters)
filtersList.forEach(filterObj => {
match = match && data[filterObj.key].toLocaleLowerCase().indexOf(filterObj.filterValue.toLocaleLowerCase()) !== -1;
});
return match;
};
So the filter would only return true if all values are matched, otherwise it returns false.
Upvotes: 5
Reputation: 2545
Declare the TableFilter interface for convenience:
interface ITableFilter {
column: string;
value: any;
}
The filter predicate function is being called once per row(!) when filtering is applied: if the predicate returns true, the current row will be included in filtered results. So we'll iterate over our filters array and return false if there's a mismatch in one of the columns.
Replace your dataSource filterPredicate with the following custom one:
setDataSource() {
this.dataSource = new MatTableDataSource(this.yourData);
this.dataSource.filterPredicate = this.customFilterPredicate;
}
customFilterPredicate(data: any, filters: ITableFilter[]): boolean {
for (let i = 0; i < filters.length; i++) {
const fitsThisFilter = data[filters[i].column].includes(filters[i].value);
if (!fitsThisFilter) {
return false;
}
}
return true;
}
Afterwards you may apply your filter like that:
// implement some relevant logic to build the filters array
const filter: ITableFilter | any = [{column: 'name', value: 'foo'}, {column: 'description', value: 'bar'}];
// this is how you apply a filter
this.dataSource.filter = filter;
It's a little bit hackish that we need to set filters as 'any'; but the filter function expects a string, and I found no other way to get around it.
Upvotes: 5
Reputation: 3703
You can do so by replacing the filterPredicate function with your own filter function and passing multiple values (as an object) to the filter.
Example code:
this.timeSlotDataSource.filterPredicate = (data: any, filterObjString: string) => {
const filterObj = JSON.parse(filterObjString);
const filterValue1 = filterObj.filterValue1;
const filterValue2 = filterObj.filterValue2;
const filterValue3 = filterObj.filterValue3;
let available = false;
// You may add additional logic here if needed
if('your own logic')
available = true;
else
return false;
return available &&
( data.slotName.toLowerCase().indexOf(filterValue1) !== -1 ||
data.slotInfo.toLowerCase().indexOf(filterValue2) !== -1 ||
data.slotTime.toLowerCase().indexOf(filterValue3) !== -1);
}
filter(){
const filterObj = {
filterValue1: this.filterValue1,
filterValue2: this.filterValue2,
filterValue3: this.filterValue3,
};
this.timeSlotDataSource.filter = JSON.stringify(filterObj);
}
Upvotes: 1
Reputation: 547
I was able to figure it out!
ngOnInit() {
this.httpService.get('./assets/emp.json').subscribe(
data => {
this.headers = data;
this.dataSource = new MatTableDataSource(this.headers);
this.initialData = this.dataSource.data;
this.dataSource.paginator = this.paginator;
// console.log(data);
},
(err: HttpErrorResponse) => {
console.log(err.message);
}
);
}
applyFilter(filterValue: string) {
let filteredData;
// let initialData = this.dataSource.data;
console.log("iniital data", this.initialData)
if (filterValue.length != 0) {
console.log("data source:", this.dataSource)
this.dataSource.filter = filterValue.trim().toLowerCase();
filteredData = this.dataSource.filteredData;
this.dataSource.data = filteredData
console.log("Filtered Data", filteredData);
} else {
this.dataSource.data = this.initialData
console.log("Its empty")
}
}
Upvotes: -2