Lattice Grid Buy a licence

demo D76

Orphans and cycles

Data that is not quite a tree. Nothing is dropped

orphans: 'Unassigned'

Building…
Loading a live grid…

The configuration

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

<div id="grid"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/lattice-grid.esm.min.js';
  import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/modules/react.esm.min.js';

  const LatticeGrid = createLatticeGrid({ React, createGrid });

  // Rows naming a parent that is not in the extract go to the named bucket, and
  // a cycle is cut and its rows shown at the root, with one console warning
  // rather than one per row. No row is lost either way.
  const tree = { parentKey: 'parentId', orphans: 'Unassigned', label: 'name', title: 'Circuit' };

  const columns = [
    { field: 'source', title: 'Source', filter: { type: 'set' }, layout: { width: 160 } },
    { field: 'parentId', title: 'Names as parent', layout: { width: 220 } },
    { field: 'charge', title: 'Monthly charge', type: 'number', format: { style: 'currency', currency: 'GBP', decimals: 0 }, total: 'sum', layout: { width: 180 } },
  ];

  const rows = [/* a parent reference with orphans and a cycle */];

  function App() {
    return (
      <LatticeGrid
        rowKey="id"
        selection="multiple"
        grandTotalRow="bottom"
        tree={tree}
        toolPanel={{ side: 'left', panels: ['columns', 'filters', 'views', 'quick'],
          actions: ['undo', 'redo', 'export', 'restore', 'maximise'], exportName: 'lattice-demo' }}
        columns={columns}
        rows={rows}
        style={{ height: '540px' }}
      />
    );
  }

  createRoot(document.getElementById('grid')).render(<App />);
</script>

Handling broken hierarchies: missing parents and cyclic references

Real hierarchy data is rarely a clean tree. A parent ID can point at a row that was filtered out upstream, deleted from the source system, or never existed in the first place, and occasionally a chain of parent references loops back on itself. A developer reaches for this when building tree data from a flat table sourced from a database join or a CSV export, where referential integrity was never guaranteed. Lattice Grid handles both cases without dropping rows: set orphans: 'Unassigned' and any row whose declared parent cannot be found is regrouped under a synthetic bucket with that label, rather than being silently excluded from the grid or, worse, from a total. Cycles are detected during tree construction, so a row that would otherwise reference its own descendant as a parent is broken out into the same fallback grouping instead of causing infinite recursion. Because this is a JavaScript data grid built for incremental updates, the orphan bucket recalculates only for the rows affected by a change, not the whole tree. This matters for accessibility as well as correctness: a screen reader announcing group counts should never encounter a total that silently excludes rows the user cannot otherwise account for.

What happens to rows with a missing or invalid parent ID in a tree grid?

Lattice Grid groups them under a fallback node rather than discarding them. Setting orphans: 'Unassigned' on the tree configuration names that node, so every row whose parent reference cannot be resolved, including rows caught in a circular reference, still appears in the grid, still counts toward totals, and remains visible to filtering and search.