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?
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;
}
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();
}
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;
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
}
}