Lattice Grid Buy a licence

showcase, hosted here

UK Trade Dashboard: Data Router and Derived Grids

What the United Kingdom imports and exports, by commodity chapter and trading partner, running live on this site: one month of figures through the Data Router, driving a table, two tabs, five KPI tiles and four charts from a single load. Source: HM Revenue & Customs, uktradeinfo.

Loading the dashboard…

two ways to use it

A grid when that is all you need. A full data layer when it is not.

Start with a grid

Point createGrid at an element and some rows, the way the grid above was made. Sorting, filtering, grouping, editing, totals and export come with it, in one file with no dependencies. For many teams that is the whole product, and it stays that simple.

const grid = LatticeGrid.createGrid(el, { columns, rows });
Get a grid on the page in a minute →

Grow into a data layer

When one feed has to drive a whole screen, add the data router. It takes the stream, decides what each record is, and keeps every grid, board and tile fed from one connection: orders to one grid, shipments to another, a third grid summarising the second, a board by region, tiles totalling it all, and a record that matches nothing counted, not lost. The picture below is that router, running.

The router API →
Live feed ws://ops 0 msg Data Router key: 'type' order shipment unrouted 0 Orders 0 rows Shipments 0 rows By carrier rollup Open orders 0 $0 In transit 0 0 units Carriers 0 busiest Shipments by region

order shipment incident, no route, counted as unrouted

The diagram above is the Data Router running on synthetic orders and shipments, so its picture never goes stale between releases; the mapping onto the dashboard above is direct. The feed is the saved month. The router is the same createDataRouter call, configured with rowKey: 'id' so a new month is a keyed diff against the one on screen. The viewers are the "Imports and exports" table the router loads directly, the Exports and Imports tabs (each a derived view of that table), the five KPI tiles and the four charts, all bound to the table or to a grid derived from it.

What the Data Router does here

One array of rows, one month at a time, goes into the router with one call: createDataRouter({ key: 'flow', rowKey: 'id' }), attached to the combined table with a predicate that accepts every row. Picking a different month in the dropdown hands that month's rows to router.load(...). Because the router carries a row key, that load is a keyed diff rather than a clear-and-refill: the chapter, partner and flow combinations the two months share are updated in place, and only the rows that actually changed value, mass or balance repaint. Nothing else on the page is told the month changed. The Exports and Imports tabs are derived views of the same table (each with its own where filter and follow: 'all', so they show their flow for whichever month the table is on, not whatever the table's own filters currently narrow it to), the five KPI tiles read the table directly, and three of the four charts bind to it too. All of them repaint from the same diff the router applied, because they are all reading the one grid the router feeds rather than being pushed a copy of the new month individually.

That is the property worth reaching for a router over a plain reload: a screen built from several views of one feed stays a screen built from several views of one feed as the feed changes, with nothing to keep in step by hand. The Data Router page covers the general case, including fanning one feed out to several destinations by key, which this page's single-destination router does not need.

How the derived trend grid works

A derived grid takes its rows from another grid instead of an array or a server: give it a source with mode: 'derived' and a from naming the grid to read, describe the shape you want back (grouped and summed, in this case), and it recomputes whenever the rows it reads from its source change. The derived source guide covers the mechanism in full; this page uses it for the one view that needs to see more than the month on screen: the trend chart.

Underneath it sits a second grid with no screen presence at all, built with createHeadlessGrid and loaded with every one of the twenty-four saved months at once, about a hundred thousand rows. The trend grid is derived from that window:

const trendGrid = createHeadlessGrid({
  rowKey: '__key',
  source: {
    mode: 'derived',
    from: windowGrid,        // the headless grid holding every saved month
    follow: 'filtered',      // only the rows the 'inView' filter matches
    groupBy: ['month', 'flow'],
    select: { value: { of: 'value', fn: 'sum' } },
    sort: [{ col: 'month', dir: 'asc' }, { col: 'flow', dir: 'asc' }],
  },
  columns: [ /* month, flow, value */ ],
});

Grouped down to one row per month and flow, that is 48 rows for the line chart to draw rather than the full window, and it re-derives itself whenever the window grid's matched rows change. follow: 'filtered' is what makes the trend follow the table rather than always covering every row: each time the table's contents change, the page reads what it currently shows and narrows the window to those exact chapter, partner and flow combinations with a named filter, windowGrid.filters.where('inView', ...). Narrow the table to one partner, or turn on the £10 million big-trade filter, and the trend narrows with it, because it is a live computation over the window's currently matched rows rather than a fixed snapshot.

The tiles and charts

The five KPI tiles read the same combined table live, following whatever it currently matches: Exports in view and Imports in view sum the value column for each flow; Trade balance in view sums the balance column, coloured green above zero and red below it; Change on the month before compares only the chapter, partner and flow combinations present in both months, so narrowing the table to one country compares that country with itself rather than against the whole of the prior month; Partners in view counts the distinct trading partners on screen.

Three of the four charts bind to the same table: a treemap of who the trade is with, a treemap of what is traded by Harmonised System section, and a horizontal bar of imports and exports by world region. The fourth, the month-by-month line chart, binds instead to the derived trend grid above, so it draws the whole saved window rather than one month at a time.

Data: HM Revenue and Customs, uktradeinfo, Open Government Licence v3.0. A saved copy taken nightly by the GitHub demo; this page reads that copy. The demo, live on GitHub Pages · its source.