Data Filtering in Apex Grid
The Apex grid supports filtering operations on its data source. Data filtering is controlled on per-column level, allowing you to have filterable and non-filterable columns. By default, filtering on a column is disabled unless explicitly configured with the filter property of the column configuration object.
Since v3.3, filterable columns show a filter button in the header that opens the filter as a floating panel (an active filter is highlighted, with a count badge when more than one condition is set), instead of the always-present inline filter row used previously.
{
key: 'price',
filter: true
}
The filter property can be either a simple boolean or a ColumnFilterConfiguration object which exposes additional configuration options:
{
key: 'price',
filter: {
/**
* For string data types controls whether the filter operations for this column will be case sensitive.
* By default, filter operations for string types are case insensitive.
*/
caseSensitive: true;
}
}
Need parentheses? A per-column filter cannot express "A and (B or C)". The Advanced Filter Builder in the enterprise package is a nested AND / OR query over any number of columns, available as a visual builder element or as plain JSON. While an advanced filter is active it owns column filtering.
Filter model
The building blocks for filter operations in the grid is the FilterExpression which has the following structure:
export interface FilterExpression<T, K extends Keys<T> = Keys<T>> {
/**
* The target column for the filter operation.
*/
key: K;
/**
* The filter function which will be executed against the data records.
*/
condition: FilterOperation<T[K]> | OperandKeys<T[K]>;
/**
* The filtering value used in the filter condition function.
*
* @remarks
* Optional for unary conditions.
*/
searchTerm?: T[K];
/**
* Dictates how this expression should resolve in the filter operation in relation to
* other expressions.
*/
criteria?: FilterCriteria;
/**
* Whether the filter operation should be case sensitive.
*
* @remarks
* If not provided, the value is resolved based on the column filter configuration (if any).
*/
caseSensitive?: boolean;
}
Filter conditions
The condition of an expression is the name of a built-in condition. Which names a column accepts depends on its type. Conditions marked unary take no searchTerm.
Column type | Conditions |
|---|---|
number, currency, rating, progress | equals, doesNotEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual, empty (unary), notEmpty (unary) |
boolean | all, true, false, empty, notEmpty (all unary) |
string (the default) and every other type, date included | contains, doesNotContain, startsWith, endsWith, equals, doesNotEqual, empty (unary), notEmpty (unary) |
The first condition in each list is the one the filter panel starts with. Text conditions are case insensitive unless the column sets filter: { caseSensitive: true } or the expression sets caseSensitive: true.
An expression whose key is not a column, or whose condition is not in its column's list, is dropped with a console warning. A greaterThan on a text column, for example, is dropped rather than applied.
The same tables are exported as StringOperands, NumberOperands and BooleanOperands, so you can read the names at runtime:
import { NumberOperands } from 'apex-grid';
Object.keys(NumberOperands); // ['equals', 'doesNotEqual', 'greaterThan', ...]
Custom conditions
When no built-in condition fits, pass a condition object instead of a name. logic receives the cell value, the searchTerm and the case sensitivity flag, and returns whether the row passes. A date column, for example, offers the text conditions, so a date range needs its own:
const after = {
name: 'after',
unary: false,
logic: (value, term) => new Date(value) > new Date(term),
};
grid.filterExpressions = [{ key: 'hireDate', condition: after, searchTerm: '2024-01-01' }];
A custom condition is saved by getState() under its name only, and setState() drops a name it does not know. If you persist state, apply custom conditions again after restoring it.
How conditions read values (3.6)
currency,ratingandprogresscolumns filter as numbers, as their column types say. The filter panel offers the number conditions and reads the typed value as a number, andgetSchema()lists the number operands for them. A saved state orfilter()call that names a text condition such ascontainson one of these columns is dropped with a console warning.- Text conditions accept any cell.
nullandundefinedread as empty text, and numbers, dates and other values match on their text, so an empty optional field no longer stops the whole filter. - Number ranges skip empty cells. Greater than, Less than and their or-equal forms no longer read
nullor an empty string as 0; use Empty to match those cells. - OR applies per column.
(city is Paris OR Oslo) AND (id is 3 OR 99)keeps the rows that match both columns. - An expression without
caseSensitivekeeps the column'sfilter.caseSensitive.
When the filter changes, the grid scrolls back to its first row. Reapplying an equal filter, such as setState(getState()), keeps the scroll position.
Filter API
The Apex grid exposes two main approaches for applying filter operations from its API. Either through the ApexGrid.filter()/ApexGrid.clearFilter() methods or through the Apex.grid.filterExpressions property.
The filter() method accepts either a single expression or an array of filter expression and then filters the grid data based on those expressions.
// Single
grid.filter({ key: 'firstName', condition: 'contains', searchTerm: 'George' });
// Multiple
grid.filter([
{ key: 'firstName', condition: 'startsWith', searchTerm: 'a' },
{ key: 'firstName', condition: 'startsWith', searchTerm: 'g', criteria: 'or' },
]);
The clearFilter() method, as the name implies, clears the filter state of a single column or the whole grid component, depending on the passed arguments.
// Clear the filter state for the `age` column.
grid.clearFilter('age');
// Clear the filter state of the grid.
grid.clearFilter();
Initial filter state
The filterExpressions property is very similar in behavior to the filter() method call. It exposes a declarative way to control filter state in the grid, but the most useful property is the ability to set initial filter state when the Apex component is first rendered.
For example here is a Lit-based sample:
{
filterState: FilterExpression[] = [
{ key: 'age', condition: 'greaterThan', searchTerm: 21 },
/** unary condition so `searchTerm` is not required */
{ key: 'active', condition: 'true' },
];
render() {
return html`<apex-grid .filterExpressions=${this.filterState}></apex-grid>`
}
}
Assigning filterExpressions replaces the whole filter, as setState does; [], null or undefined clears it. Before 3.6 an assignment merged into the current filter, so code that assigned one expression at a time to build up a filter should assign the full list, or call filter(), which still adds. The list applies at the next update, against the columns as they are then, so a template may bind it before columns.
It can be used to get the current filter state of the component and do additional processing depending on another state in your application.
const state = grid.filterExpressions;
// Save the current filter state
saveUserFilterState(state);
Events
When a filter operation is performed through the UI, the component emits a custom filtering event. The detail property holds the target column key, the filter expressions which will be applied by the Apex grid, and the type of change ('add', 'modify' or 'remove'). The event is cancellable and if cancelled will prevent the current filter operation.
After the grid applies the new filter state, a filtered event is emitted. It contains the filter state for the column which was the target of the operation and it is not cancellable.
grid.addEventListener('filtering', (event: CustomEvent<ApexFilteringEvent<T>>) => { ... });
grid.addEventListener('filtered', (event: CustomEvent<ApexFilteredEvent<T>>) => { ... });
Remote filter operations
In cases where filtering must be done remotely or you want to save the current state/data to a server somewhere, the Apex grid exposes a hook where you can implement and customize this behavior.
Using the dataPipelineConfiguration property, you can provide a custom hook which will be called each time a filter operation is about to run. The callback is passed a DataPipelineParams object.
export type DataPipelineParams<T extends object> = {
/**
* The current data state of the grid.
*/
data: T[];
/**
* The grid component itself.
*/
grid: ApexGrid<T>;
/**
* The type of data operation being performed.
*/
type: 'sort' | 'filter';
};
grid.dataPipelineConfiguration = { filter: (params: DataPipelineParams<T>) => T[] | Promise<T[]> };
The custom callback can be async as the grid will wait for it until it resolves.
The following example mocks remote filter operation, reflecting the REST endpoint generated based on the filter state of the component.