demo D99
A bounded window: JavaScript Data Grid Demo
A never-ending feed held to a span of time, with a chart that keeps scrolling through the quiet
source.maxAge
This grid holds a rolling span of time from a never-ending feed, ageing rows out on a clock instead of a row count. The chart beside it keeps scrolling through a quiet spell rather than freezing, because its window ends at now, not at the newest row.
The configuration
<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.css">
<script src="https://cdn.latticegrid.dev/lattice-grid@1.103.2/lattice-grid.min.js"></script>
<div id="grid" style="height: 540px"></div>
<script>
const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
rowKey: 'id',
selection: 'multiple',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
exportName: 'lattice-demo',
},
statusBar: { panels: ['rowCount', 'progress', 'updates', 'selectedCount'] },
// The stream never ends, so without maxRows this grid would grow until the
// tab died overnight. The window drops the oldest rows as new ones arrive,
// and the physical index stays put while it does: a selection made ten
// thousand rows ago still points at its row.
source: {
mode: 'stream',
maxRows: 20000,
open: (req) => openEndlessStream({ signal: req.signal }), // a stream with no end
},
columns: [
{ field: 'symbol', title: 'Symbol', layout: { width: 130, pin: 'start' } },
{ field: 'desk', title: 'Desk', filter: { type: 'set' } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'price', title: 'Price', type: 'number', layout: { width: 120 },
format: { decimals: 4 }, filter: { type: 'number' } },
{ field: 'change', title: 'Change', type: 'number', layout: { width: 110 },
format: { style: 'percent', decimals: 2 } },
{ field: 'volume', title: 'Volume', type: 'number', layout: { width: 120 },
format: { notation: 'compact' }, filter: { type: 'number' } },
{ field: 'notional', title: 'Notional', type: 'number', layout: { width: 150 },
format: { style: 'currency', currency: 'USD', notation: 'compact' } },
{ field: 'status', title: 'Status', filter: { type: 'set' }, layout: { width: 110 },
cell: { decoration: 'pill', variant: { map: { open: 'success', halted: 'danger', settled: 'neutral' } } } },
],
// No rows: the window fills from the endless stream, capped at maxRows.
});
</script>
Keeping a live feed to a span of time, not a row count
A time window trims a continuously updating source down to whatever span of the recent past a view actually needs: the last six seconds on a trading ticket, the last hour on a log tail, the last shift on a sensor feed. Lattice Grid controls it with source.maxAge, a number of milliseconds after which a row is aged out regardless of how many others have arrived since, so the window means the same thing whether the feed is running hot or has gone quiet. ageBy names the column that carries a row’s own time, a column id, a dotted path or a function, for a log or event feed where the row’s moment matters more than when it happened to reach the grid; leave it out and rows age from the moment they arrived, which suits a metric or market feed with no timestamp of its own. A row whose time cannot be read is never aged out, so a malformed value costs nothing rather than quietly dropping a row nobody meant to lose.
A chart reading the same grid can carry the identical window on its x axis with window: { kind: 'time', span }, and that is what makes the difference visible: the axis domain ends at the current moment rather than at the newest point plotted, so the chart keeps scrolling left through a lull in the feed instead of stopping dead until the next row breaks the silence. A row count alone cannot do this, because nothing arrives during a quiet spell for it to evict, and a chart tied to the newest row simply stops moving. Ageing by the clock and drawing by the clock are two ends of the same idea: the window is what a reader would call “the last few seconds”, not an artifact of how busy the feed happens to be right now.
How do I keep a live-updating grid and chart to the last few seconds, rather than the last N rows?
Set source.maxAge to the span, in milliseconds, you want the grid to hold, with ageBy if the rows carry their own timestamp and otherwise left out to age from arrival. Give a chart reading the same data axis: { x: { window: { kind: 'time', span } } } on its time axis so its domain rolls forward with the clock and keeps scrolling even while the feed is silent, rather than freezing on the last point drawn.