demo D287
A feed that survives a reload in Angular
The routed rows written to the browser’s durable store and read straight back on the next visit, beside a fast feed held to a steady repaint rate so a firehose lands smoothly
router.persist() · router.restore()
This writes the routed rows to the browser's durable store and reads them straight back on the next visit, so a screen built on a live feed is not empty after a reload. Alongside it, a fast feed is held to a steady repaint rate so a firehose lands smoothly.
This is the Angular version. A standalone component takes the whole configuration through one config input, surfaces grid events as outputs, and exposes the live grid on a getter for anything the inputs do not cover.
The configuration
import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { createGrid } from '@toclocoinc/lattice-grid';
import '@toclocoinc/lattice-grid/css';
import { createDataRouter } from '@toclocoinc/lattice-grid/modules/data-router';
import { LatticeGridComponent, LatticeRouter, provideLattice, provideLatticeRouter } from '@toclocoinc/lattice-grid/angular';
const MONEY = { style: 'currency', currency: 'USD', decimals: 0 };
@Component({
selector: 'app-root',
standalone: true,
imports: [LatticeGridComponent],
providers: [provideLatticeRouter({ key: 'type', rowKey: 'id' })],
template:
'<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">' +
' <lattice-grid route="order" [routeOptions]="{ label: 'orders' }" [config]="orderConfig" style="display:block;height:340px"></lattice-grid>' +
// Held to four repaints a second: the updates that arrive in between are
// folded into the next one.
' <lattice-grid route="sensor" [routeOptions]="{ label: 'sensors', backpressure: { maxHz: 4 } }" [config]="sensorConfig" style="display:block;height:340px"></lattice-grid>' +
'</div>',
})
export class AppComponent {
private routerService = inject(LatticeRouter);
orderConfig = {
rowKey: 'id',
columns: [
{ field: 'id', title: 'Ref', layout: { width: 110, pin: 'start' } },
{ field: 'region', title: 'Region', filter: { type: 'set' } },
{ field: 'value', title: 'Value', type: 'number', format: MONEY, total: 'sum' },
],
};
sensorConfig = {
rowKey: 'id',
columns: [
{ field: 'id', title: 'Sensor', layout: { width: 120, pin: 'start' } },
{ field: 'reading', title: 'Reading', type: 'number' },
],
};
constructor() {
// Write the routed rows to the browser's own durable store, then ask for
// the last session back before any new event arrives. On a first visit
// restore() returns false, so seed; on a reload the orders return from
// storage.
this.routerService.router.persist({ key: 'my-desk' });
this.routerService.router.restore().then((restored: boolean) => {
if (!restored) this.routerService.router.load([/* rows tagged type: 'order' | 'sensor' */]);
});
setInterval(() => this.routerService.router.apply([{ op: 'upsert', row: { id: 'O-' + Date.now(), type: 'order', region: 'EMEA', value: 500 } }]), 2000);
setInterval(() => this.routerService.router.apply([{ op: 'upsert', row: { id: 'S-1', type: 'sensor', reading: Math.random() * 100 } }]), 120);
}
}
bootstrapApplication(AppComponent, {
providers: [provideLattice({ createGrid, createDataRouter })],
});
A live desk that comes back exactly where you left it
A screen built on a live feed usually starts empty and waits for the feed to refill it. Reload the page and the desk you were reading is gone until the next batch arrives. This keeps it. As routed rows arrive they are written to the browser’s own durable store, so when the page loads again the desk comes back exactly where you left it, before a single new event has landed. Reload the tab and the orders are already there.
The same router that fans one feed out to several views is the one doing the saving, so nothing extra sits between the feed and the screen. Name a key to store under and the routed rows are kept up to date as they change; ask for them back on load and they return. Where the browser will not keep durable storage, the router simply runs in memory and fills from the feed as before, so the page still works, it just does not resume.
A fast feed gets its own treatment. Point a route at a firehose and hold its view to a fixed number of repaints a second, and the updates that arrive in between are folded into the next one. The grid stays smooth under a feed that would otherwise make it stutter, and the count of updates merged away tells you how much work the view was spared. One feed, then, that both survives a reload and takes a firehose without flinching.
How do I make a live feed survive a page reload?
Turn on persistence on the data router: call router.persist with a key to store under, and the routed rows are written to the browser’s durable store as they arrive. On the next load, call router.restore before you start the feed, and the views come back filled from storage. For a fast route, pass a backpressure option when you attach its view, capping how often it repaints, and the router coalesces the updates in between into one. Read the coalesced count from the router’s own metrics to see how many updates were merged away.