demo D142
N of M rows
Three counts that are not interchangeable, and which one is shown
rows.count() · matchCount() · totalCount()
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>
// Three counts read fresh on every paint via a cell renderer, because a
// computed column would memoise and freeze at the first values painted.
const countCell = (read) => ({
render: (p) => {
const n = read(p.grid.rows);
return Number.isFinite(n) ? n.toLocaleString() : '';
},
});
const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
rowKey: 'id',
statusBar: true,
grandTotalRow: true,
toolPanel: { side: 'left', panels: ['columns', 'filters', 'quick'], actions: ['restore'], exportName: 'lattice-counts' },
columns: [
{ id: 'displayCount', title: 'rows.count()',
layout: { pin: 'start', width: 150 }, sort: false, filter: false,
cell: countCell((rows) => rows.count()) },
{ id: 'matchCount', title: 'matchCount()',
layout: { pin: 'start', width: 150 }, sort: false, filter: false,
cell: countCell((rows) => rows.matchCount()) },
{ id: 'totalCount', title: 'totalCount()',
layout: { pin: 'start', width: 150 }, sort: false, filter: false,
cell: countCell((rows) => rows.totalCount()) },
{ field: 'team', title: 'Team', group: true, filter: { type: 'set' } },
{ field: 'service', title: 'Service', layout: { flex: 1, min: 180 } },
{ field: 'errorRate', title: 'Error rate', type: 'number', format: { style: 'percent', decimals: 2 }, filter: { type: 'number' } },
{ field: 'monthlyCost', title: 'Monthly cost', type: 'number', format: { style: 'currency', currency: 'USD' }, total: 'sum' },
{ field: 'headcount', title: 'Headcount', type: 'number', total: 'sum' },
],
rows, // 10,000 service records
});
</script>
Showing the right row count when rows are filtered, grouped or paged
A grid displaying “N of M rows” makes a claim that only holds if N and M mean the same thing everywhere else in the interface. A JavaScript data grid with filters and paging active at once has three distinct counts: how many rows exist in the source, how many survive the current filter, and how many are visible on the current page. Collapsing those into one figure misleads a user who filtered a ten-thousand-row set down to forty and expects the footer to say forty, not ten thousand. Lattice Grid keeps the three separate through rows.count(), matchCount() and totalCount(), so a footer can quote whichever is relevant without recomputing anything itself. Reach for this whenever a UI needs to confirm a filter did something, or a pager needs a page count derived from the filtered set rather than the raw one.
All three counts are maintained incrementally as rows are added, removed or re-filtered, rather than recalculated by scanning the row store on each call, so reading matchCount() after a filter change is a constant-time lookup regardless of source size. The status text built from these counts renders in a live region, so a screen reader announces the new “40 of 10,000” figure the moment a filter applies, without the user needing to locate and re-read the footer.
How do I show “N of M rows” correctly when a grid is filtered?
Read matchCount() for the number of rows currently passing the active filters and totalCount() for the unfiltered source total, rather than deriving either from the visible page. Lattice Grid updates both automatically as filters change, so a footer built from them stays correct without a manual recount, and rows.count() is available separately for the count of rows currently rendered in the viewport.