Lattice Grid Buy a licence

demo D60

Facets for a server source

Supplying counts from an API when the grid holds one page

facets.provider

Building…
Loading a live grid…

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.css">
<script src="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.13.0/lattice-grid.min.js"></script>

<div id="grid" style="height: 540px"></div>

<script>
  const grid = LatticeGrid.createGrid(document.getElementById('grid'), {
    rowKey: 'id',
    toolPanel: { side: 'left', panels: ['filters', 'columns'], actions: ['restore'], exportName: 'costs' },
    columns: [
      { field: 'account', title: 'Account', filter: { type: 'set' } },
      { field: 'service', title: 'Service', filter: { type: 'set' } },
      { field: 'resource', title: 'Resource', layout: { flex: 1, min: 200, max: 320 } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      { field: 'environment', title: 'Env', filter: { type: 'set' } },
      { field: 'cost', title: 'Monthly cost', type: 'number', total: 'sum', format: { style: 'currency', currency: 'USD' } },
    ],
    // The header charts come from the server, not the grid's own store: one call
    // per column per filter change, handed the other columns' filters so the bars
    // move. This is the contract a paged or remote source has to meet.
    facets: {
      enabled: true,
      collapsed: false,
      provider: async ({ column, filters }) => {
        const res = await fetch('/api/facets', {
          method: 'POST',
          headers: { 'content-type': 'application/json' },
          body: JSON.stringify({ column, filters }),
        });
        return res.json();  // [{ value, count }] per category, or buckets for a number
      },
    },
  });
</script>

Facet counts for a server-side row model

A facet is the count beside each option in a set filter: 42 next to “Open”, 118 next to “Closed”. Computing that is trivial when a JavaScript data grid holds the full dataset in memory, since counting distinct values is a single pass over rows already resident on the client. It stops being trivial under a server-side row model, where the grid only holds the page it is currently showing and has no way to count values it has never fetched. Lattice Grid reaches for facets.provider in that situation: a function the grid calls with the active filter state and the column being faceted, returning the counts an API computes against the full table rather than the visible slice.

The provider is asked again whenever a sibling filter changes, so counts stay consistent with cross-filtering rules even though the grid cannot see the rows behind them. A request is only issued for the column a user has opened, not for every filterable column on every keystroke, which keeps a quick-typed text filter from firing a facet query per character. The set filter’s checkbox list renders from whatever the provider last returned, and each option stays reachable by keyboard while a request is in flight.

How do you show filter counts when the grid does not hold all the rows?

Configure facets.provider with a function that takes the current filter tree and target column, and returns counts from the server rather than the client. Lattice Grid calls it each time a sibling filter changes and renders the result in the set filter’s checkbox list, so counts reflect the full dataset even though only one page of rows is loaded locally.