FilterPlugin
FilterPlugin and AsyncFilterPlugin integrate filtering with reactive state and export adapters.
The FilterPlugin integrates ZenGrid’s filtering functionality with the reactive store system, supporting frontend filtering, backend delegation, and multiple export adapters.
Overview
FilterPlugin runs at phase 20 and provides:
- Reactive filter state management
- Frontend in-memory filtering
- Backend filter delegation
- Export adapters (REST, GraphQL, SQL)
- Filter caching and indexing
- Column-based filtering
FilterPlugin depends on CorePlugin and must be registered after it.
Creation
Create the FilterPlugin using factory functions:
import { createFilterPlugin, createAsyncFilterPlugin } from '@zengrid/core';
// Synchronous filteringconst filterPlugin = createFilterPlugin();grid.usePlugin(filterPlugin);
// Asynchronous filteringconst asyncFilterPlugin = createAsyncFilterPlugin();grid.usePlugin(asyncFilterPlugin);Frontend Mode
Frontend mode applies filters in-memory:
import { createGrid, createFilterPlugin } from '@zengrid/core';
const grid = createGrid(container, { columns: [ { id: 'name', field: 'name', filterable: true }, { id: 'age', field: 'age', filterable: true } ], data: users, filtering: { mode: 'frontend' }});
grid.usePlugin(createFilterPlugin());
// Apply filtersgrid.api.filter.setFilters([ { field: 'age', operator: 'gt', value: 18 }]);Filter Operators
Built-in operators for filtering:
// String operators{ field: 'name', operator: 'contains', value: 'John' }{ field: 'name', operator: 'startsWith', value: 'J' }{ field: 'name', operator: 'endsWith', value: 'son' }{ field: 'name', operator: 'equals', value: 'John' }
// Number operators{ field: 'age', operator: 'gt', value: 18 }{ field: 'age', operator: 'gte', value: 18 }{ field: 'age', operator: 'lt', value: 65 }{ field: 'age', operator: 'lte', value: 65 }{ field: 'age', operator: 'equals', value: 25 }
// Array operators{ field: 'status', operator: 'in', value: ['active', 'pending'] }{ field: 'status', operator: 'notIn', value: ['deleted'] }
// Range operators{ field: 'date', operator: 'between', value: [start, end] }Backend Mode
Backend mode delegates filtering to a callback:
const grid = createGrid(container, { columns: [ { id: 'name', field: 'name', filterable: true } ], filtering: { mode: 'backend', onFilterRequest: async (filters) => { const response = await fetch('/api/users', { method: 'POST', body: JSON.stringify({ filters }) }); const data = await response.json(); grid.api.data.setData(data); } }});
grid.usePlugin(createFilterPlugin());Export Adapters
FilterPlugin includes adapters for exporting filter state to different formats:
REST Adapter
import { RESTFilterAdapter } from '@zengrid/core';
const adapter = new RESTFilterAdapter();const filters = [ { field: 'age', operator: 'gt', value: 18 }, { field: 'status', operator: 'equals', value: 'active' }];
const queryParams = adapter.export(filters);// Result: "?age_gt=18&status=active"
const url = `/api/users${queryParams}`;GraphQL Adapter
import { GraphQLFilterAdapter } from '@zengrid/core';
const adapter = new GraphQLFilterAdapter();const filters = [ { field: 'age', operator: 'gt', value: 18 }];
const graphqlFilter = adapter.export(filters);// Result: { where: { age: { _gt: 18 } } }
const query = ` query GetUsers($filter: UserFilter) { users(filter: $filter) { id name age } }`;SQL Adapter
import { SQLFilterAdapter } from '@zengrid/core';
const adapter = new SQLFilterAdapter();const filters = [ { field: 'age', operator: 'gt', value: 18 }, { field: 'name', operator: 'contains', value: 'John' }];
const { whereClause, params } = adapter.export(filters);// whereClause: "age > ? AND name LIKE ?"// params: [18, '%John%']
const sql = `SELECT * FROM users WHERE ${whereClause}`;Use export adapters to automatically convert filter state to your backend API format.
AsyncFilterPlugin
For async filtering with loading states:
import { createAsyncFilterPlugin } from '@zengrid/core';
const grid = createGrid(container, { filtering: { mode: 'backend', onFilterRequest: async (filters) => { grid.api.loading.show();
try { const data = await fetchFilteredData(filters); grid.api.data.setData(data); } finally { grid.api.loading.hide(); } } }});
grid.usePlugin(createAsyncFilterPlugin());Reactive State
FilterPlugin creates these reactive signals:
// Current filter statestore.get('filter.state') // Array<FilterCondition>store.set('filter.state', filters)
// Computed signalsstore.get('filter.hasFilters') // Boolean: any active filtersstore.get('filter.filteredCount') // Number: rows after filteringstore.get('filter.filteredFields') // Array<string>: filtered field namesAPI Methods
FilterPlugin registers these methods on grid.api.filter:
// Set filtersgrid.api.filter.setFilters([ { field: 'age', operator: 'gt', value: 18 }]);
// Get current filtersconst filters = grid.api.filter.getFilters();
// Add filtergrid.api.filter.addFilter({ field: 'status', operator: 'equals', value: 'active' });
// Remove filtergrid.api.filter.removeFilter('age');
// Clear all filtersgrid.api.filter.clearFilters();
// Get filtered countconst count = grid.api.filter.getFilteredCount();Filter State Structure
interface FilterCondition { field: string; operator: FilterOperator; value: any;}
type FilterOperator = | 'equals' | 'notEquals' | 'gt' | 'gte' | 'lt' | 'lte' | 'contains' | 'startsWith' | 'endsWith' | 'in' | 'notIn' | 'between';Filter Caching
FilterPlugin includes caching for performance:
// Cache filters for repeated queriesconst grid = createGrid(container, { filtering: { mode: 'frontend', cacheEnabled: true, cacheSize: 100 // LRU cache size }});
// Repeated filters use cached resultsgrid.api.filter.setFilters([ { field: 'age', operator: 'gt', value: 18 }]); // Executes filter
grid.api.filter.setFilters([ { field: 'age', operator: 'gt', value: 18 }]); // Uses cached resultFilter caching uses an LRU cache from @zengrid/shared to avoid memory leaks.
Column Indexing
FilterPlugin creates indexes for frequently filtered columns:
const grid = createGrid(container, { filtering: { mode: 'frontend', indexedColumns: ['status', 'category'] }});
// Indexed columns filter faster with O(1) lookupsgrid.api.filter.setFilters([ { field: 'status', operator: 'equals', value: 'active' }]); // Fast index lookupEvents
FilterPlugin emits events for filter changes:
grid.on('filter:change', (filters) => { console.log('Filters changed:', filters);});
grid.on('filter:clear', () => { console.log('Filters cleared');});Performance Optimization
FilterPlugin includes several optimizations:
- Filter compilation: Compiles filter expressions once
- Range optimization: Optimizes range filters for sorted data
- Substring optimization: Uses efficient string matching
- Index-based filtering: Uses column indexes when available
Enable column indexing for frequently filtered columns to improve performance.