Floating Filters
A row of inputs under the column headers, one per column, always visible — so the reader can narrow two columns without opening anything. One option turns it on, and the same column definitions drive the popup and code-driven modes too.
Filter row — every filter visible at once
filterMode: 'row'. An input per column under the header, and an empty slot where a column declares no filter. Type in two of them and watch the line underneath: that object is exactly what your data source receives.
No filters applied.
One option turns it on. Every column that declares a filter gets an input; a column
that does not gets an empty slot rather than shifting its neighbours, so the row
stays lined up with the header. Last Name is that gap.
filterMode: 'row'
Each input carries a funnel of its own
The box in the row edits the value. The operator lives behind the funnel next to it — which is where Email's narrowed list (contains and equals, and nothing else) becomes visible. So the row is not a smaller popup: it is the value editor pulled out in front, with the rest one click away.
What reaches your server
The line under the grid is the live (filterChange) payload, and it is the same object
your data source is handed as params.filters — keyed by column, each entry an
operator and a value. Nothing is filtered behind your back on the server-side path: the grid tells
you what the reader asked for, and your endpoint decides what it means.
Header popup — the same editors, on demand
filterMode: 'popup'. The identical column definitions, with no row: a funnel icon per header opens the same editor, plus the operator list. Nothing about the columns changed — only where the editors live.
The same seven columns, the same seven filter declarations, one line of config
different. There is no row now — each filterable header carries a funnel, and clicking it opens
the same editor plus the operator list.
filterMode: 'popup'
Worth choosing deliberately: the row costs a permanent strip of vertical space and shows every active filter at a glance; the popup gives that space back and hides the fact that a filter is on behind a highlighted funnel. Filtering as the main task wants the row. Occasional filtering on a crowded screen wants the popup.
No filter UI — driven from your own code
filterMode left out, which is the default. This grid has no filter chrome at all and opens filtered to Engineering — the column definitions are untouched, the values just come from your code instead of from a reader.
filterMode is left out here — that is the default, and it means no filter chrome at
all. No row, no funnels. What it does not mean is that the columns lost anything: the
seven filter declarations are identical to the two examples above.
This grid opens showing Engineering only, and the filter behind that came from code:
readonly initialState: GridStateSnapshot = {
filters: { department: { op: 'equals', value: 'Engineering' } },
};
The object is the shape the reader's typing would have produced, and it reaches your data source the same way. Use this when the filters belong to your own screen — a sidebar, a set of chips, a route parameter — rather than to the grid. See External Filters for wiring those controls up, and Grid State for saving and restoring a whole view.
The same columns, three chromes
filterMode changes where the editors appear, never what they are. The
filter you declare on a column is identical in all three examples above — the
columns block in every code tab is the same block — so switching costs one line and no column
definitions.
- row — inputs under the header. Best when filtering is the main task.
- popup — a funnel icon per header opens the same editor. Best when screen space is tight or filters are occasional.
- omitted — no filter UI at all. This is the default; you drive the values
from your own controls or from
[initialState].
There is no 'none' to pass. Leaving the option out is the third mode, which
is why the type reads 'row' | 'popup' and the default column of the table below says
omitted.
Choosing the editor per column
type: 'text'— free text, withplaceholderfor a hint.type: 'number'— numeric input; comparisons stay numeric.type: 'select'— a fixedoptionslist, for columns with a known set of values.type: 'date'— a date picker.operators— narrow what a column offers. Email allows only contains and equals; open its funnel in either of the first two examples to see a two-item list where the other text columns show the full one.
The filter row is one option and needs no extra markup, so it composes with everything else on this page — sorting, resizing, reordering and infinite scroll are on in all three examples. Drag a column in the first one and its filter input travels with it.
Filter row options
| Property | Type | Default | Description |
|---|---|---|---|
| config.filterMode | 'row' | 'popup' | omitted — no filter UI | Where the filter editors appear. 'row' is the always-visible row under the header; 'popup' is a funnel icon per column. Leave it out and there is no filter UI, without removing the columns' filter definitions. |
| column.filter | ColumnFilterConfig | — | Declares that a column can be filtered, and which editor it uses. A column without it leaves an empty slot in the row. |
| column.filter.type | 'text' | 'number' | 'date' | 'select' | 'text' | Which editor to show for this column. |
| column.filter.placeholder | string | — | Hint text inside the input. |
| column.filter.options | { label, value }[] | — | Choices for type: 'select'. |
| column.filter.operators | string[] | all for the type | Narrow the operators this column offers. The list appears behind the funnel — in row mode the box itself edits the value only. |
| [initialState] | GridStateSnapshot | — | Opens the grid with filters (and sorts, column state, page) already applied — the only way to filter a grid that renders no filter UI. |
| (filterChange) | EventEmitter<FilterState> | — | Fires on every committed change, with the same object your data source receives as params.filters. |
| messages.filterFor | string | 'Filter {col}' | Screen-reader label for each input in the row; {col} is replaced with the column name. |