Lattice Grid Buy a licence

demo D70

Totals of the view or the data

The same grid filtered, totals answering each question in turn

totalFilteredOnly: false

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',
    /*
     * The default, `true`, is the answer to "what am I looking at". `false`
     * is the answer to "what is there", and says the filter is a lens rather
     * than a selection. Both the grand total and every group total follow
     * the setting, and the count on the grand total row follows the total,
     * so it never reports fewer rows than the number covers.
     */
    totalFilteredOnly: true,
    grandTotalRow: 'bottom',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'views', 'quick'],
      actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
      exportName: 'lattice-demo',
    },
    columns: [
      { field: 'account', title: 'Account', filter: { type: 'set' }, group: { enabled: true, index: 0 } },
      { field: 'service', title: 'Service', filter: { type: 'set' } },
      { field: 'resource', title: 'Resource', layout: { flex: 1, min: 200, max: 320 } },
      { field: 'environment', title: 'Env', filter: { type: 'set' },
        cell: { decoration: 'pill', variant: { map: {
          prod: 'danger', 'prod-2': 'danger', 'definitely-prod': 'danger',
          staging: 'warning', untagged: 'warning', test: 'info',
          dev: 'success', 'not-prod': 'neutral',
        } } } },
      { field: 'cost', title: 'Monthly cost', type: 'number', layout: { width: 170 },
        format: { style: 'currency', currency: 'USD', decimals: 2 }, total: 'sum' },
    ],
    rows,  // cloud cost rows, one object per resource
  });

  // Narrow to production, then switch the totals between the view and the data.
  const production = { col: 'environment', op: 'in', value: ['prod', 'prod-2', 'definitely-prod'] };
  grid.filters.set(production);
  grid.set('totalFilteredOnly', false);
  // Setting the same filter again is what drops the cached totals.
  grid.filters.set(production);
</script>

Choosing whether a total reflects the filtered view or the full dataset

A grand total or group footer can answer two different questions, and a JavaScript data grid has to let a developer pick which one before the figure means anything. “What do the rows in front of the user add up to” is a different sum from “what do all the rows add up to, including the ones a filter is currently hiding”, and reports built on the wrong one erode trust quickly. Lattice Grid exposes the choice as totalFilteredOnly, a boolean read alongside the aggregation itself: set it false and a total keeps summing every row in the dataset regardless of the active filter, set it true and the same total narrows to only the rows currently matching. Because the setting sits next to the reducer rather than forcing a second query or a manual recompute, switching between the two views does not touch the underlying data or trigger a reload. A finance screen showing “total spend across the account” alongside “total spend in the current filter” can display both figures from the one dataset, each wired to its own totalFilteredOnly value, with the difference between them visible to the user as the filter changes.

Should a grid’s grand total include filtered-out rows?

It depends on what the total is meant to represent. Set totalFilteredOnly: false when the figure should stay anchored to the whole dataset, such as a running account balance. Set it true when the figure should track only what the user can currently see, such as “total of the rows matching this search”. Lattice Grid supports both from the same column definition.