Filter Events
Filter events let application code react after visible rows change, adjust the rows that will be trimmed, or cancel local evaluation and delegate a request to a backend.
Local Core event sequence
Section titled “Local Core event sequence”A normal Core filter run emits these filter-plugin events in order:
| Order | Event | Cancelable | Important runtime detail |
|---|---|---|---|
| 1 | beforefilterapply | Yes | collection, full filterItems, source, and columns |
| 2 | beforefiltertrimmed | Yes | Filter model plus physical itemsToFilter indexes |
| 3 | afterfilterapply | No | multiFilterItems, original source, and first-condition collection |
Canceling beforefilterapply stops evaluation. Canceling beforefiltertrimmed stops the calculated filter trim from being installed. beforetrimmed and aftertrimmed are general grid events and can also run for grouping, tree visibility, or application-owned trims.
The Core filter plugin writes its calculated trim through the data provider, so its normal apply path does not emit the public beforetrimmed and aftertrimmed element events. Those events wrap calls through the grid’s general setTrimmed() method; do not include them when reasoning about the filter-plugin sequence.
filterconfigchanged is different: it reports a new grid filter configuration and is not a per-run completion event.
Read visible rows after filtering
Section titled “Read visible rows after filtering”afterfilterapply.detail.source is the original local source, not the filtered result. Read the provider-backed visible models with getVisibleSource():
grid.addEventListener('afterfilterapply', async (event) => { const visibleRows = await grid.getVisibleSource(); const detail = ( event as CustomEvent<{ multiFilterItems: Record<string, unknown[]>; }> ).detail;
console.log('Active filters', detail.multiFilterItems); console.log('Visible rows', visibleRows.length);});The cast is intentional. The runtime Core plugin emits afterfilterapply, but the generated Core element event map in some current packages does not declare that event yet, so TypeScript may otherwise infer only the base Event type. Applications can also augment HTMLRevoGridElementEventMap centrally with the runtime detail type.
Modify the calculated trim
Section titled “Modify the calculated trim”Use beforefiltertrimmed when the application needs final control over physical row indexes:
grid.addEventListener('beforefiltertrimmed', (event) => { // Keep physical row 0 visible even when ordinary conditions reject it. delete event.detail.itemsToFilter[0];});Use this hook sparingly. A custom filter predicate or canonical AST condition is easier to persist and transport when the requirement is ordinary business logic.
Cancel and delegate to a backend
Section titled “Cancel and delegate to a backend”Prevent beforefilterapply to stop local trimming, then load rows from the server. Guard the source replacement so reapplying the same active model does not create a request loop:
let loadedSignature = '';let requestId = 0;
grid.addEventListener('beforefilterapply', async (event) => { const detail = ( event as CustomEvent<{ filterItems: Record<string, unknown[]>; filterAst?: unknown; executionContext?: unknown; }> ).detail; const transportState = detail.filterAst ? { filterAst: detail.filterAst, executionContext: detail.executionContext, } : { filterItems: detail.filterItems }; const signature = JSON.stringify(transportState, (_key, value) => value instanceof Set ? [...value] : value, );
if (signature === loadedSignature) return;
event.preventDefault(); const activeRequest = ++requestId; const response = await fetch('/api/orders/filter', { method: 'POST', headers: { 'content-type': 'application/json' }, body: signature, }); const rows = await response.json();
if (activeRequest !== requestId) return; loadedSignature = signature; grid.source = rows;});With AdvanceFilterPlugin, set filter.external: true when the backend always owns filtering. The plugin then prevents local trimming itself while still emitting the same beforefilterapply payload. Your handler only needs to send the model and install the latest response; it should not emit afterfilterapply, because that event remains a local-filter completion signal.
grid.filter = { external: true };grid.addEventListener('beforefilterapply', async (event) => { const { filterAst, executionContext } = event.detail; grid.source = await loadOrders({ filterAst, executionContext });});In production, also handle request failures and abort stale network work. The Pro Pagination, Infinity Scroll, and Server-Side Grouping plugins already implement this delegation flow.
Prefer Pro’s canonical filterAst for new remote integrations. Compatible filterItems can contain runtime values such as the selection filter’s excluded-value Set; the fallback above converts sets to arrays, but applications should still define and validate their complete wire contract. See Filter State and Presets.
Pro AST events
Section titled “Pro AST events”AdvanceFilterPlugin emits canonical events in addition to the Core lifecycle:
| Event | Detail | Use it for |
|---|---|---|
filterastchange | filterAst, executionContext, origin, projectable | Persisting or transporting the effective canonical tree |
filterasterror | Attempted AST, diagnostics, and origin | Showing validation errors without applying an invalid tree |
For a local Pro run, filterastchange is emitted before beforefilterapply. Pro also adds filterAst, executionContext, and temporal transport context to the Core before/after event details at runtime. Origins distinguish config, api, ui, quickFilter, and clear changes.
grid.addEventListener('filterastchange', (event) => { console.log(event.detail.origin, event.detail.filterAst);});
grid.addEventListener('filterasterror', (event) => { console.error(event.detail.diagnostics);});Quick Filter events
Section titled “Quick Filter events”Changing grid.quickFilter wraps the local filter run with two Pro events:
beforequickfilterapply → filterastchange → beforefilterapply → beforefiltertrimmed → afterfilterapplyafterquickfilterapplybeforequickfilterapply is cancelable. Its detail and afterquickfilterapply contain the normalized quickFilter, source, and columns. Remote Pro data plugins cancel the local quick-filter run and emit the completion event after the latest remote response wins.
Common mistakes
Section titled “Common mistakes”- Do not treat
aftertrimmedas filter-only; other grid features also trim rows. - Do not read the original
sourcefield as the filtered result; callgetVisibleSource(). - Do not perform a remote request without
preventDefault()or local filtering will run too. - Do not assign remote rows without a loop guard and stale-request protection.
- Do not mutate
beforequickfilterapply.detail.quickFilterexpecting it to replace the property value; observe it or cancel and assign a newgrid.quickFiltervalue. - Do not assume generated element typings expose every plugin-emitted runtime field. Narrow or augment event detail types where needed.