Lattice Grid Buy a licence

demo D170

Charts follow the grid in htmx

Filter, sort or edit the grid and every chart bound to it redraws

createChart({ grid, type, x, y })

This draws several charts from a grid and has every one of them redraw when you filter, sort or edit the grid. It shows that a chart reads the grid's live filtered rows, not a snapshot, so a filter on the table moves the whole dashboard with it.

Building…
Loading a live grid…

This is the htmx version. A server-rendered table is upgraded in place, so the page is readable before any script runs and gains the full grid when the module loads, which suits a progressively enhanced app.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">

<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; height: 210px">
  <div id="by-region"></div>
  <div id="by-status"></div>
  <div id="scatter"></div>
</div>
<!-- A real, server-rendered table: readable with JavaScript off. data-field on
     each header names the column that cell becomes, and data-id on each row is
     the key the grid carries it under. -->
<table id="charts-follow-the-grid-table" data-lattice-grid>
  <thead>
    <tr>
      <th data-field="circuit">Circuit</th>
      <th data-field="region">Region</th>
      <th data-field="status">Status</th>
      <th data-field="bandwidth">Bandwidth</th>
      <th data-field="utilisation">Utilisation</th>
      <th data-field="charge">Monthly charge</th>
    </tr>
  </thead>
  <tbody>
    <tr data-id="0"><td>DFB-EDI-100000</td><td>Edinburgh</td><td>live</td><td>100</td><td>41.2</td><td>94.88</td></tr>
    <tr data-id="1"><td>MPL-BIR-100001</td><td>Birmingham</td><td>provisioning</td><td>10000</td><td>67.5</td><td>4324.6</td></tr>
    <tr data-id="2"><td>DFB-LON-100002</td><td>London</td><td>live</td><td>2000</td><td>83.9</td><td>1031.02</td></tr>
    <!-- the rest of the rows, rendered by your server -->
  </tbody>
</table>

<script type="module">
  import { hydrateTable } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/htmx.esm.min.js';
  import { createChart } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/charts.esm.min.js';

  // The markup carries the rows; a server-rendered table's column types are
  // declared in the config hydrateTable takes. The grid replaces the table
  // where it stood.
  const grid = hydrateTable(document.getElementById('charts-follow-the-grid-table'), {
    columns: [
      { field: 'circuit', title: 'Circuit', layout: { width: 180 } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      { field: 'status', title: 'Status', filter: { type: 'set' } },
      { field: 'bandwidth', title: 'Bandwidth', type: 'number' },
      { field: 'utilisation', title: 'Utilisation', type: 'number', format: { decimals: 1, suffix: '%' } },
      { field: 'charge', title: 'Monthly charge', type: 'number',
        format: { style: 'currency', currency: 'GBP' }, total: 'sum' },
    ],
  });

  // Three charts on one grid: each reads the same rows and follows the sort and
  // filters in place, so a filter set on the table redraws all three at once.
  createChart({ grid, container: '#by-region', type: 'bar',     x: 'region',    y: 'charge' });
  createChart({ grid, container: '#by-status', type: 'donut',   x: 'status',    y: 'charge' });
  createChart({ grid, container: '#scatter',   type: 'scatter', x: 'bandwidth', y: 'charge' });
</script>

One selection, seen two ways at once

The charts module draws every chart type from a grid’s own data, and the point of this demo is the smallest one: a chart reads the grid’s filtered rows, not a snapshot taken when it was created. Filter the grid from the rail, sort a column, edit a value, and every chart bound to that grid redraws on the next frame. There is nothing to subscribe to and nothing to keep in step, because a chart of rows the user cannot see would be describing a different data set.

A chart is one call: createChart({ grid, container, type, x, y }), where x names the category column and y the measure. A measure that needs reducing is written y: { col: 'charge', fn: 'sum' }. The module imports nothing from the grid core - the grid is handed in - so the drawing code loads only on a page that actually charts, and a grid that never charts never pays for it.

The three charts above the grid read the same 2,000 circuits it does: charge summed by region, charge split by status, and every circuit’s charge against how full its pipe runs. Narrow the grid to one region and watch all three follow.

How does a chart stay in step with a filtered grid?

It does not have to be kept in step. createChart binds the chart to the grid, and the chart listens to the grid’s own change events - filter, sort, edit, group - and redraws from the currently visible rows on the next animation frame. You filter or sort the grid as normal; the chart follows without any code wiring the two together. See the step-by-step how to draw a chart that follows the grid’s filters guide for a minimal, runnable example with a quick-filter box and a click-to-filter bar, plus the source on GitHub.