demo D62
An application-level filter
A host predicate composed with the grid’s own, for permissions
hostFilter: { active, passes }
The configuration
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.css">
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.js"></script>
<div id="grid" style="height: 540px"></div>
<script>
const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
rowKey: 'id',
toolPanel: { side: 'left', panels: ['filters', 'columns'], actions: ['restore'], exportName: 'lattice-demo' },
columns: [
{ field: 'account', title: 'Account', filter: { type: 'set' } },
{ field: 'service', title: 'Service', filter: { type: 'set' } },
{ field: 'resource', title: 'Resource', layout: { flex: 1, min: 200, max: 320 } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'environment', title: 'Env', filter: { type: 'set' },
cell: { decoration: 'pill', variant: { map: {
prod: 'danger', 'prod-2': 'danger', 'definitely-prod': 'danger',
staging: 'warning', untagged: 'warning', test: 'info',
dev: 'success', 'not-prod': 'neutral',
} } } },
{ field: 'change', title: 'Change', type: 'number',
layout: { width: 140, min: 140 }, format: { style: 'percent', decimals: 1 } },
{ field: 'cost', title: 'Monthly cost', type: 'number', layout: { width: 170 },
format: { style: 'currency', currency: 'USD', decimals: 2 }, total: 'sum' },
],
// The application's own predicate, composed with whatever the user filters.
// Rows it refuses never reach the row model: not in the totals, not in an
// export, and not reachable by clearing the filters. `passes` is handed the
// grid's row wrapper, so the record itself is `row.data`. In a real
// deployment this reads a session rather than a constant.
hostFilter: {
active: () => true,
passes: (row) => row.data.environment !== 'prod' && row.data.environment !== 'prod-2',
},
rows, // 20,000 cloud cost rows
});
</script>
Composing a host-supplied filter with the grid’s own
Some rows need to stay hidden regardless of what a user selects in the filter menu: a support agent scoped to one region, a viewer role without access to archived records, a tenant boundary in a multi-tenant table. Rather than pre-slicing the dataset before it reaches the grid, Lattice Grid accepts a hostFilter: { active, passes } option that runs alongside whatever the user configures. The active flag turns the predicate on or off, and passes receives each row and returns a boolean, exactly like a permissions check written outside the grid. Both filters apply to the same row model, combining with AND: a row must clear both to render.
This is the pattern to reach for whenever filtering needs to encode something the user cannot override, as distinct from set filters or the filter builder, which are user-facing. Because passes runs during the grid’s own filter pass rather than as a separate pre-processing step, it benefits from the same virtual scrolling the rest of the grid uses: only rows that clear the host predicate and any user conditions get measured and rendered, so a 200,000-row dataset with a permissions boundary costs no more to scroll than one without it. The predicate re-evaluates whenever active changes or the data updates, keeping the boundary consistent as rows are added or edited.
How do you hide rows a user is not allowed to see in a JavaScript data grid?
Set hostFilter.active to true and supply a passes(row) function that returns false for any row the current user should not see. Lattice Grid combines this with the user’s own filters using AND, so the restriction holds regardless of what conditions someone sets in the filter menu, and it re-runs automatically as data or permissions change.