External Filters
Filter the grid from controls of your own — a chip row, a date range, a toggle. You own the rows or the request, so no grid hook is involved.
Filter controls outside the grid
A chip row above the grid picks the department. The grid is handed the rows that survive — sorting and the filter row still work on top.
Sort by Salary first, then pick another department — the sort stays.
The chips own the array. Picking a department filters the rows in the component, and the grid is handed a data source over what is left — it is never told a filter happened.
Everything the grid does still works on top of that. Sort by Salary, then pick another department: the sort survives, because a new data source reloads the rows without resetting the view you chose. Type in the First Name box and both filters apply at once.
A date range picker of your own
Two date inputs, one range. Set From later than To to see the empty state a real user would hit.
A column filter carries one operator and one value, so "between these two dates" cannot be a single column filter. Two inputs of your own can, and the pair travels to your API as two parameters.
Set From later than To and the grid shows its empty state — the range is legal, it simply matches nothing. That is the same empty state your users will see, so it is worth looking at.
Showing only my rows
One switch, one request parameter. Leave it on and type in the First Name box — your control and the grid's filter both apply.
You are signed in as someone in Sales, so "my rows" means the rows of that team. Turn the switch on and the grid reloads with them.
This is the case where sending your own parameter beats writing a filter. Your API already knows
who is signed in, so mine=true is enough — the browser never has to name the user, and
nobody can widen the result by editing a filter value.
Leave the switch on and type in the First Name box: the toggle narrows the rows, the column filter narrows them again, and the sort applies to what is left.
There is no external-filter hook, and you do not need one
A filter that lives outside the grid is not a grid feature. You already own the two things it needs: the array of rows, or the request that fetches them.
- Own the array — filter it yourself and hand the result to the grid.
- Own the request — add your own parameters to the call your data source makes.
Either way you build a new GridDataSource when your control changes. Give the grid a
new data source and it reloads. That is the whole contract — no registration, no callback pair, no
"filter changed" method to remember.
Building the data source in a computed
Keep each control in a signal() and derive the data source from them. The grid reloads
the moment the computed produces a new value, so nothing else has to be wired.
readonly department = signal('All');
readonly dataSource = computed(() => this.api.forFilters({
department: this.department() === 'All' ? undefined : this.department(),
}));
In the template: [dataSource]="dataSource()". A new data source starts a fresh view,
so the grid loads from the top. The sort the user clicked and any column filters they typed stay
as they were.
Your controls and the grid's own filters both apply
The two do not compete. Your control decides which rows the data source works from; the grid's
filter row and sort then apply to those rows, and reach your data source as
params.filters and params.sorts exactly as before.
Make sure your control can actually be used
These are your controls, so their accessibility is yours too. Give every one a visible label, keep
it reachable with Tab, and leave the focus ring alone. A chip row is a group of
buttons with aria-pressed, not a row of clickable divs.
What an external filter touches
| Property | Type | Default | Description |
|---|---|---|---|
| [dataSource] | GridDataSource<T> | — | Give it a new object and the grid reloads from the top. This is how an external filter reaches the grid. |
| params.filters | FilterState | — | The grid's own column filters. Untouched by your controls — merge, do not replace. |
| params.sorts | SortConfig[] | [] | Survives a data-source swap, so a reader who sorted keeps their sort when a chip changes. |
| params.offset / params.limit | number | — | Paging window. Apply your external filter before slicing, or the page counts are wrong. |
| config.filterMode | 'row' | 'popup' | 'row' | The grid's own filter UI. It keeps working alongside your controls. |