Chapter 32 of 34

Live data without rebuilding the table

1,500 cell updates per second – only the changed, visible cells are redrawn.

  • ⏱ 5 min
  • ▶ Video episode 032
  • git tag ep-032
  • needs @guiexpert/table@1.1.89

After this chapter you can

  • update single cells with updateCells()
  • flash a cell with CSS classes
  • keep permanent colours in the model

Many small changes

A ticker, a monitoring view, a trading screen: values change many times per second, but the rows stay the same. Rebuilding the table – or even repainting all visible cells – for every change would waste most of the frame.

updateCells: value + redraw of exactly these cells

src/model/ticker.ts
import {
ColumnDef, type ColumnDefIf, type TableApi, TableCellUpdateEvent, TableFactory, type TableModelIf,
allRight, bodyLeft, px100, px120, px200
} from "@guiexpert/table";
export interface Quote { symbol: string; name: string; price: number; change: number; volume: number }
const QUOTE_COLUMNS: ColumnDefIf[] = [
new ColumnDef("symbol", "Symbol", px100, bodyLeft),
new ColumnDef("name", "Name", px200, bodyLeft),
new ColumnDef("price", "Price", px120, allRight), // column 2
new ColumnDef("change", "Change %", px120, allRight), // column 3
new ColumnDef("volume", "Volume", px120, allRight) // column 4
];
export function createQuoteModel(quotes: Quote[]): TableModelIf {
return TableFactory.createTableModel({ rows: quotes, columnDefs: QUOTE_COLUMNS });
}
/** One tick: some quotes move – only their cells are updated. Returns the number of changed cells. */
export function tick(api: TableApi, quotes: Quote[], moves: number): number {
const events: TableCellUpdateEvent[] = [];
for (let n = 0; n < moves; n++) {
const row = Math.floor(Math.random() * quotes.length);
const q = quotes[row];
const price = Math.max(1, Math.round(q.price * (1 + (Math.random() - 0.5) / 50) * 100) / 100);
const change = Math.round((price - q.price) / q.price * 10000) / 100;
const volume = q.volume + Math.floor(Math.random() * 500);
const up = change >= 0;
events.push(
new TableCellUpdateEvent("body", row, 2, price, { "tick-up": up, "tick-down": !up }),
new TableCellUpdateEvent("body", row, 3, change, { "tick-up": up, "tick-down": !up }),
new TableCellUpdateEvent("body", row, 4, volume));
}
api.updateCells(events); // writes the values into the model, redraws only these cells
return events.length;
}
let cells = 0;
let timer = window.setInterval(() => cells += tick(api, quotes, 50), 100); // 50 quotes every 100 ms
  • A TableCellUpdateEvent = area, row, column, new value, and optionally CSS classes for this cell.
  • TableApi.updateCells() writes the values into the model and redraws only the affected cells – if they are visible. Cells off screen just get the value and are drawn when you scroll there.
  • updateCells(events, true) repaints the whole table instead.
Live 1,000 quotes, 1,500 updates per second
Screenshot: 1,000 quotes, 1,500 updates per second Screenshot: 1,000 quotes, 1,500 updates per second
Green = up, pink = down. Scroll while it runs – the frame rate stays at 60 fps.

Where the data comes from

WebSocket, Server-Sent Events, polling – collect the changes and call updateCells once per animation frame or interval, not once per message. The showcase Crypto does that with real price data.

Next

A table that remembers sorting and scroll position: chapter 33 (Persist state).

API used in this chapter

Showcases