I have a generic grid like below
<table class="table">
<thead>
<tr role="row" class="headers-filters">
<th *ngFor="let objGColumn of gridColumns; let idx=index;">
<dt-column-header [(filter)]="grdFilters[objGColumn.propertyKey]"></dt-column-header>
</th>
</tr>
</thead>
<tbody>
<tr role="row" *ngFor="let objRC of recordList | filterColumn: grdFilters; let idx=index;">
<td class="text-center" *ngFor="let objGColumn of gridColumns; let idx=index;">
<span [textContent]="objRC[objGColumn.propertyKey]"></span>
</td>
</tr>
</tbody>
</table>
Where gridColumns will be array of display columns.
dt-column-header is a component which will contain textbox for search column. It has two-way binding property filter which will be filters in grid
filters will be like grdFilters = {LastName: "123", FirstName: "456"}
My pipe is like below. I have taken reference from https://www.code-sample.com/2018/07/angular-6-search-filter-pipe-table-by.html
@Pipe({
name: 'filterColumn'
})
export class GrdFilterPipe implements PipeTransform {
transform(items: any, filter: any, defaultFilter: boolean): any {
if (!filter){
return items;
}
if (!Array.isArray(items)){
return items;
}
if (filter && Array.isArray(items)) {
let filterKeys = Object.keys(filter);
if (defaultFilter) {
return items.filter(item =>
filterKeys.reduce((x, keyName) =>
(x && new RegExp(filter[keyName], 'gi').test(item[keyName])) || filter[keyName] == "", true));
}
else {
return items.filter(item => {
return filterKeys.some((keyName) => {
return new RegExp(filter[keyName], 'gi').test(item[keyName]) || filter[keyName] == "";
});
});
}
}
}
}
My first issue is pipe is not calling when gridFilters change. I had to make it impure which will have effect on performance.
Another issue is pipe works fine with {LastName: "123"}
OR {FirstName: "456"}
But not working with {LastName: "123", FirstName: "456"}
OR {LastName: "", FirstName: "456"}
OR {LastName: "123", FirstName: ""}
"My first issue is pipe is not calling when gridFilters change":
RECOMENDED: Leave your pipe as a pure pipe and reassign gridFilters
instead of modifying it. In other words, instead of using ngModel in your dt-column-header
component, use input event and reassign gridFilters
variable.
OR (NOT RECOMMENDED): Make your pipe an impure pipe by adding pure: false
in the @Pipe
decorator:
@Pipe({
name: 'searchFilter',
pure: false
})
See this StackBlitz DEMO with a working example code.
"Another issue is pipe works fine with... But not working with":
The problem is with the pipe's code:
return items.filter(item => {
return filterKeys.some((keyName) => {
return new RegExp(filter[keyName], 'gi').test(item[keyName]) || filter[keyName] == "";
});
});
Apparently you didn't get the outcome you've wanted to get. The problem that you didn't mentioned the result that you want to get, what do you mean working/not working? edit your question with your needs and I'll update my answer.
explaining pure/impure pipes:
Your pipe is a pure pipe (default pipe type when not using metadata pure: false
in @Pipe decorator). A pure pipe will execute only when there is either a change to a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object).
Therefore, you have 2 options as a solution:
Solution 1 (recomended):
In order for your pipe to call again when gridFilters changes, you need to reassign it instead of modifying it. In other words, instead of using ngModel in your dt-column-header
component, use input event and reassign gridFilters
variable.
Solution 2 (not recomended):
Make your pipe an impure pipe by adding pure: false
in the @Pipe
decorator:
@Pipe({
name: 'searchFilter',
pure: false
})
This time, your pipe will execute in every component change detection cycle. This is also why this solution is not recommend because it will be called often, as often as every keystroke or mouse-move.
Angular Docs - Impure pipes says:
With that concern in mind, implement an impure pipe with great care. An expensive, long-running pipe could destroy the user experience.