demo D81
Lazy children
Fetching a branch on expand, with an abort when it closes first
tree.loadChildren
The configuration
<script>
import { createGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.esm.min.js';
import createLatticeAction from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/modules/svelte.esm.min.js';
const lattice = createLatticeAction({ createGrid });
const config = {
rowKey: 'id',
selection: 'multiple',
toolPanel: {
side: 'left',
panels: ['columns', 'filters', 'views', 'quick'],
actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
exportName: 'lattice-demo',
},
tree: {
parentKey: 'parentId',
label: 'name',
title: 'Region',
// Without this the roots would have no expander at all: they hold no
// children, so there would be no gesture left to fetch any. A node that
// declares children reads as closed until they arrive.
hasChildren: (data) => data.childCount > 0,
// Return the branch's children, honouring the abort signal so a branch
// closed before it lands stays openable rather than left empty.
loadChildren: (row, signal) => fetchChildren(row.data, signal),
},
columns: [
{ field: 'kind', title: 'Level', filter: { type: 'set' }, layout: { width: 120 } },
{ field: 'childCount', title: 'Children', type: 'number', layout: { width: 120 } },
{ field: 'devices', title: 'Devices', type: 'number', total: 'sum', layout: { width: 120 } },
{ field: 'charge', title: 'Monthly charge', type: 'number', format: { style: 'currency', currency: 'GBP', decimals: 0 }, total: 'sum', layout: { width: 180 } },
],
rows, // eight roots, nothing built below them until a branch is opened
};
</script>
<svelte:head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.27.0/lattice-grid.min.css">
</svelte:head>
<div use:lattice={config} style="height: 540px"></div>
Fetching tree branches on demand
Lazy children load a branch of a tree only when a row is expanded, rather than pulling the whole hierarchy up front. A developer reaches for this when the source is a remote API rather than an in-memory array: an org chart where each manager’s direct reports live behind a separate endpoint, a file browser where a folder’s contents are unknown until it is opened, a category tree too large to fetch in one request. Lattice Grid calls tree.loadChildren for the row being expanded, passing its id, and expects a promise resolving to that row’s children; the branch renders once the promise settles, and a distinct loading state occupies the row in between so the grid never presents an empty branch as an empty result. Closing a branch before its fetch resolves aborts the in-flight request through the same signal loadChildren receives, so a fast double-click through several folders does not leave stale fetches racing to populate rows the user has already collapsed. Because this is a JavaScript data grid built around incremental rendering, only the expanded branch pays the network cost; sibling rows and the rest of the tree keep their existing state untouched. The loading row exposes aria-busy while a fetch is pending, so a screen reader announces that a branch is populating rather than reporting silence.
How do I load tree children from an API only when a row expands?
Implement tree.loadChildren, returning a promise that resolves to the array of child rows for the expanded row’s id. Lattice Grid calls it on expand, shows a loading row until the promise resolves, and aborts the request if the branch is collapsed again before the fetch completes, so no children are rendered for a branch the user has already closed.