Grid: isse with removing filters programmatically
Description
I'm using excel style filtering and I want to remove one filter programmatically using this code:
clear(filter: Filter) {
const expressions = this.grid.filteringService.getExpressions(filter.name);
if (expressions.length > 1) {
const idx = expressions.findIndex(
(e) =>
e.expression.fieldName === filter.name &&
e.expression.condition.name === filter.condition &&
e.expression.searchVal === filter.value
);
this.grid.filteringService.removeExpression(filter.name, idx);
this.grid.filteringService.filterInternal(filter.name);
} else {
this.grid.clearFilter(filter.name);
}
}
This works the first time, but then not anymore.
- igniteui-angular version: 19.2.x
Steps to reproduce
- Go to https://stackblitz.com/edit/ckznrukw
- Filter two Product Names
- Remove one of the filters using the clear button (works)
- Add one filter again
- Remove one of the filters
This time both filters are removed.
Attachments
After looking into the provided sample, I've noticed that you are using the grid's filteringService, which is marked as hidden. As such, it is used internally in our code, and we do not recommend using it or the methods inside it, as this could lead to unexpected behavior, as in your scenario. As an alternative approach and in order to use only the public API, I could suggest checking and modifying the grid filteringExpressionsTree. A demo that demonstrates a possible solution could be found here. Please test it on your side and let me know if you need any further assistance.
Hi @teodosiah Thanks for the provided example. This works fine for binary filters, but not for unary filters. How would the correct implementation look like for this case?
I tried this which seems to work, but is this the correct way?
clear(filter: Filter) {
let expressions = this.grid.filteringExpressionsTree.filteringOperands?.find(operand => operand.fieldName === filter.name);
// I added this if
if (
expressions instanceof FilteringExpressionsTree &&
expressions.filteringOperands?.length === 1 &&
expressions.filteringOperands[0] instanceof FilteringExpressionsTree &&
expressions.filteringOperands[0].filteringOperands?.length > 1
) {
expressions = expressions.filteringOperands[0];
}
// ####
if (expressions instanceof FilteringExpressionsTree && expressions.filteringOperands?.length > 1) {
const idx = expressions.filteringOperands.findIndex(
(e) =>
'conditionName' in e &&
e.fieldName === filter.name &&
e.conditionName === filter.condition &&
e.searchVal === filter.value
);
expressions.filteringOperands.splice(idx, 1);
this.grid.filteringExpressionsTree = {...this.grid.filteringExpressionsTree};
} else {
this.grid.clearFilter(filter.name);
}
}
The previous approach would not work as expected for all filters applied through the custom filtering dialog (not only unary) because, in this case, there is only one filtering operand on the top level of the filtering expression tree. The approach you have implemented seems to work as expected and could be used, however, it could be modified a little to the following if it looks better to you:
clear(filter: Filter) {
const expressions = this.grid.filteringExpressionsTree.filteringOperands?.find(operand => operand.fieldName === filter.name && operand instanceof FilteringExpressionsTree);
if (expressions instanceof FilteringExpressionsTree && expressions.filteringOperands?.length > 0) {
const searchedExpressions = expressions.filteringOperands?.length > 1 ? expressions : expressions.filteringOperands[0];
let idx = -1;
if (searchedExpressions instanceof FilteringExpressionsTree && searchedExpressions.filteringOperands?.length > 1) {
idx = searchedExpressions.filteringOperands.findIndex(
(e) =>
'conditionName' in e &&
e.fieldName === filter.name &&
e.conditionName === filter.condition &&
e.searchVal === filter.value
);
if (idx > -1) {
searchedExpressions.filteringOperands.splice(idx, 1);
this.grid.filteringExpressionsTree = {...this.grid.filteringExpressionsTree};
return;
}
}
}
this.grid.clearFilter(filter.name);
}
@teodosiah Thanks. I assume I need some kind of recursive logic if the filter is more nested like this? (field null OR (field startsWith 'test' AND field endsWith '1' )
This seems to be working, I don't know if it can be written simpler.
clear(filter: Filter) {
const expressions =
this.grid.filteringExpressionsTree.filteringOperands?.find(
(operand) => operand.fieldName === filter.name
);
if (
expressions instanceof FilteringExpressionsTree &&
expressions.filteringOperands?.length > 0
) {
if (this.removeExpression(filter, expressions)) {
this.grid.clearFilter(filter.name);
} else {
this.grid.filteringExpressionsTree = {
...this.grid.filteringExpressionsTree,
};
}
return;
}
this.grid.clearFilter(filter.name);
}
removeExpression(filter: Filter, expressions: FilteringExpressionsTree) {
for (let i = 0; i < expressions.filteringOperands.length; ) {
const e = expressions.filteringOperands[i];
if (
'conditionName' in e &&
e.fieldName === filter.name &&
e.conditionName === filter.condition &&
e.searchVal === filter.value
) {
expressions.filteringOperands.splice(i, 1);
return expressions.filteringOperands.length === 0;
} else if (e instanceof FilteringExpressionsTree) {
if (this.removeExpression(filter, e)) {
expressions.filteringOperands.splice(i, 1);
continue;
}
}
i++;
}
return false;
}