This page describes the old way of declaring custom filter components when the grid option enableFilterHandlers is not set. It is strongly recommended to instead use the new behaviour described on the Filter Component page.
The example below shows two custom filters. The first is on the Athlete column and demonstrates a filter with "fuzzy" matching and the second is on the Year column with preset options.
"use client";
import React, {
useCallback,
useMemo,
useRef,
useState,
StrictMode,
} from "react";
import { createRoot } from "react-dom/client";
import { AgGridReact, AgGridProvider } from "ag-grid-react";
import "./style.css";
import {
ClientSideRowModelModule,
ColDef,
ColGroupDef,
CustomFilterModule,
GridApi,
GridOptions,
ModuleRegistry,
enableDevValidations,
} from "ag-grid-community";
import PersonFilter from "./personFilter.tsx";
import YearFilter from "./yearFilter.tsx";
import { IOlympicData } from "./interfaces";
import { useFetchJson } from "./useFetchJson";
if (process.env.NODE_ENV !== "production") {
// Enable extended validations only for development
enableDevValidations();
}
const modules = [CustomFilterModule, ClientSideRowModelModule];
const GridExample = () => {
const containerStyle = useMemo(() => ({ width: "100%", height: "100%" }), []);
const gridStyle = useMemo(() => ({ height: "100%", width: "100%" }), []);
const [columnDefs, setColumnDefs] = useState<ColDef[]>([
{ field: "athlete", minWidth: 150, filter: PersonFilter },
{ field: "year", minWidth: 130, filter: YearFilter },
{ field: "country", minWidth: 150 },
{ field: "sport" },
{ field: "gold" },
{ field: "silver" },
{ field: "bronze" },
{ field: "total" },
]);
const defaultColDef = useMemo<ColDef>(() => {
return {
flex: 1,
minWidth: 100,
};
}, []);
const { data, loading } = useFetchJson<IOlympicData>(
"https://www.ag-grid.com/example-assets/olympic-winners.json",
);
return (
<AgGridProvider modules={modules}>
<div style={containerStyle}>
<div style={gridStyle}>
<AgGridReact<IOlympicData>
rowData={data}
loading={loading}
columnDefs={columnDefs}
defaultColDef={defaultColDef}
/>
</div>
</div>
</AgGridProvider>
);
};
const root = createRoot(document.getElementById("root")!);
root.render(
<StrictMode>
<GridExample />
</StrictMode>,
);
.person-filter {
padding: 12px;
width: 200px;
}
.person-filter > div:first-child {
font-weight: bold;
}
.person-filter > div:not(:last-child) {
margin-bottom: 8px;
}
.year-filter {
width: 200px;
}
.year-filter > * {
margin: 8px 12px;
}
.year-filter > div:first-child {
font-weight: bold;
}
.year-filter > label {
display: inline-block;
}
import React, { useCallback, useRef } from 'react';
import type { IAfterGuiAttachedParams, IDoesFilterPassParams } from 'ag-grid-community';
import type { CustomFilterProps } from 'ag-grid-react';
import { useGridFilter } from 'ag-grid-react';
export default ({ model, onModelChange, getValue }: CustomFilterProps) => {
const refInput = useRef<HTMLInputElement>(null);
const doesFilterPass = useCallback(
(params: IDoesFilterPassParams) => {
const { node } = params;
const filterText: string = model;
const value: string = getValue(node).toString().toLowerCase();
// make sure each word passes separately, ie search for firstname, lastname
return filterText
.toLowerCase()
.split(' ')
.every((filterWord) => value.indexOf(filterWord) >= 0);
},
[model]
);
const afterGuiAttached = useCallback((params?: IAfterGuiAttachedParams) => {
if (!params || !params.suppressFocus) {
// Focus the input element for keyboard navigation.
// Can't do this in an effect,
// as the component is not recreated when hidden and then shown again
refInput.current?.focus();
}
}, []);
// register filter handlers with the grid
useGridFilter({
doesFilterPass,
afterGuiAttached,
});
return (
<div className="person-filter">
<div>Custom Athlete Filter</div>
<div>
<input
ref={refInput}
type="text"
value={model || ''}
onChange={({ target: { value } }) => onModelChange(value === '' ? null : value)}
placeholder="Full name search..."
/>
</div>
<div>
This filter does partial word search on multiple words, eg "mich phel" still brings back Michael Phelps.
</div>
</div>
);
};
import type { ChangeEvent } from 'react';
import React, { useCallback } from 'react';
import type { IDoesFilterPassParams } from 'ag-grid-community';
import type { CustomFilterProps } from 'ag-grid-react';
import { useGridFilter } from 'ag-grid-react';
export default ({ model, onModelChange }: CustomFilterProps) => {
const doesFilterPass = useCallback((params: IDoesFilterPassParams) => {
// doesFilterPass only gets called if the filter is active,
// which is when the model is not null (e.g. >= 2010 in this case)
return params.data.year >= 2010;
}, []);
// register filter handlers with the grid
useGridFilter({
doesFilterPass,
});
const onYearChange = ({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
onModelChange(value === 'All' ? null : value);
};
return (
<div className="year-filter">
<div>Select Year Range</div>
<label>
<input type="radio" name="year" value="All" checked={model == null} onChange={onYearChange} /> All
</label>
<label>
<input type="radio" name="year" value="2010" checked={model != null} onChange={onYearChange} /> Since
2010
</label>
</div>
);
};
export interface IOlympicData {
athlete: string,
age: number,
country: string,
year: number,
date: string,
sport: string,
gold: number,
silver: number,
bronze: number,
total: number
} import { useState, useEffect } from 'react';
/**
* Fetch example Json data
* Not recommended for production use!
*/
export const useFetchJson = <T,>(url:string, limit?: number) => {
const [data, setData] = useState<T[]>();
const [loading, setLoading] = useState(true);
useEffect(() => {
// StrictMode runs this effect twice: drop the superseded run's response rather than applying both.
let cancelled = false;
const fetchData = async () => {
setLoading(true);
// Note error handling is omitted here for brevity
const response = await fetch(url);
const json = await response.json();
const data = limit ? json.slice(0, limit) : json;
if (cancelled) {
return;
}
setData(data);
setLoading(false);
};
fetchData();
return () => {
cancelled = true;
};
}, [url, limit]);
return { data, loading };
}; Implementing a Filter Component Copy Link
Custom filter components are controlled components, which receive a filter model as part of the props, and pass model updates back to the grid via the onModelChange callback. A filter model of null means that no filter is applied (the filter displays as inactive). Note that the filter is applied immediately when onModelChange is called.
To implement the filtering logic, a custom filter needs to implement the doesFilterPass callback, and provide it to the useGridFilter hook.
export default ({ model, onModelChange, getValue }) => {
const doesFilterPass = useCallback(({ node }) => {
// filtering logic
return getValue(node).contains(model);
}, [model]);
// register filter callbacks with the grid
useGridFilter({ doesFilterPass });
return (
<div>
<input
type="text"
value={model || ''}
onChange={({ target: { value }}) => onModelChange(value === '' ? null : value)}
/>
</div>
);
}In previous versions of the grid, custom components were declared in an imperative way. See Migrating to Use reactiveCustomComponents for details on how to migrate to the current format.
Custom Filter Parameters Copy Link
Filter Props Copy Link
The following props are passed to the custom filter components (CustomFilterProps interface). If custom props are provided via the colDef.filterParams property, these will be additionally added to the props object, overriding items of the same name if a name clash exists.
The current filter model for the component. |
Callback that should be called every time the model in the component changes. |
Callback that can be optionally called every time the filter UI changes. The grid will respond with emitting a FilterModifiedEvent. Apart from emitting the event, the grid takes no further action.
|
The column this filter is for. |
The column definition for the column. |
Get the cell value for the given row node and column, which can be the column ID, definition, or Column object. If no column is provided, the column this filter is on will be used.
|
A function callback, call with a node to be told whether the node passes all filters except the current filter. This is useful if you want to only present to the user values that this filter can filter given the status of the other filters. The set filter uses this to remove from the list, items that are no longer available due to the state of other filters (like Excel type filtering).
|
The grid api. |
Application context as set on gridOptions.context. |
Filter Callbacks Copy Link
The following callbacks can be passed to the useGridFilter hook (CustomFilterCallbacks interface). The hook must be used for filters to work. The doesFilterPass callback is mandatory, but all others are optional.
Note that doesFilterPass is only called with the Client-Side Row Model. If being used exclusively with other row models, it can just return true as the filtering logic is performed on the server.
The grid will ask each active filter, in turn, whether each row in the grid passes. If any filter fails, then the row will be excluded from the final set. The method is provided a params object with attributes node (the rodNode the grid creates that wraps the data) and data (the data object that you provided to the grid for that row). Note that this is only called for the Client-Side Row Model, and can just return true if being used exclusively with other row models.
|
Optional: A hook to perform any necessary operation just after the GUI for this component has been rendered on the screen. If a parent popup is closed and reopened (e.g. for filters), this method is called each time the component is shown. This is useful for any logic that requires attachment before executing, such as putting focus on a particular DOM element.
|
Optional: A hook to perform any necessary operation just after the GUI for this component has been removed from the screen. If a parent popup is opened and closed (e.g. for filters), this method is called each time the component is hidden. This is useful for any logic to reset the UI state back to the model before the component is reopened.
|
Optional: Gets called when new rows are inserted into the grid. If the filter needs to change its state after rows are loaded, it can do it here. For example the set filters uses this to update the list of available values to select from (e.g. 'Ireland', 'UK' etc for Country filter). To get the list of available values from within this method from the Client Side Row Model, use gridApi.forEachLeafNode(callback).
|
Optional: Called whenever any filter is changed. |
Optional: Used by AG Grid when rendering floating filters and there isn't a floating filter associated for this filter, this will happen if you create a custom filter and NOT a custom floating filter.
|