developer guide
JavaScript Data Grid Filtering
Filtering runs from the header menu and from code through the same condition tree, so what a reader builds by clicking is exactly what your application can set, read back and store. The operators cover text, numbers, dates, blanks and lists of values, combined with and and or to any depth.
Developer guide › Sorting, filtering and find › JavaScript Data Grid Filtering
Filtering
There are two filters and they are separate on purpose. The quick filter is one string matched across every readable column. The filter set is a structured condition tree.
Quick filter
grid.filters.quick('singapore');
grid.filters.quick(''); // clear
A condition tree
grid.filters.set({
op: 'and',
conditions: [
{ col: 'region', op: 'in', value: ['EMEA', 'APAC'] },
{ col: 'utilisation', op: 'gt', value: 0.9 },
{ op: 'or', conditions: [
{ col: 'slaBreached', op: 'eq', value: true },
{ col: 'margin', op: 'lt', value: 0 },
]},
],
});
That structure is a published wire protocol, not an internal detail. It is what
grid.state.get() serialises, what a saved view carries, and what you can send to
a server to evaluate the same filter against the full dataset. A remote source hands it
straight to your backend.
Operators are per family: text has contains, startsWith,
matches; numbers and dates have between, gt,
lte; multi-value columns have containsAny,
containsAll, containsNone. blank and
notBlank work everywhere.
Filters your application owns: where
A condition tree can only test what is in a column. Plenty of real filters cannot be written that way - whether this user may see the row, whether you hold an exchange rate for its currency, whether it came back from your last search call. Those go in as named predicates, and they compose with everything above.
A permission filter and a toggle, side by side
grid.filters.where('visibleToMe', row => row.owner === me, { pinned: true });
grid.filters.where('rateKnown', row => rates.has(row.ccy), { deps: ['ccy'] });
grid.filters.where(); // ['visibleToMe', 'rateKnown']
grid.filters.where('rateKnown', null); // remove just that one
grid.filters.reapply('rateKnown'); // the rate table arrived late
There is deliberately no "a filter is present" flag. That flag is a second piece of state describing the first, and the two drift: the classic symptom is a grid that filters while the UI insists it is not, or insists it is filtering while every row passes. Here, registering a predicate is what puts it in force, and removing it is what takes it out.
Three options shape one. deps names the columns the predicate reads, exactly as
value.deps does for a computed column: the verdict is then cached per row and
re-run when one of those columns changes on that row, not when an unrelated one does.
Leave it off and the predicate is assumed to read the whole row, so it runs every pass and can
never be stale. pinned makes a predicate survive
filters.clear(), which is what you want for permissions and tenant scoping and
not much else. condition gives the predicate a declarative twin that is pushed to
the source while the function stays as the residual, so a pushdown engine narrows the fetch
instead of your code filtering a page.
Migrating from AG Grid's external filter: its three pieces become two.
isExternalFilterPresent() goes away, because registration is presence.
doesExternalFilterPass(node) becomes the predicate itself.
onFilterChanged() becomes deps where the grid can watch the change
for you, and reapply(name?) where it cannot.
What travels in a saved view is the name, not the function.
state.get() carries where: string[]; your predicates are your code
and the grid will not pretend it can serialise them. Applying a view that names a predicate
you have not registered reports the skip instead of quietly showing a wider row set,
and never removes a predicate the view did not mention.