Lattice Grid Buy a licence

demo D78

Master-detail

A row expanding into a nested grid, several open at once

detail: { rows, config }

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',
    selection: 'multiple',
    toolPanel: {
      side: 'left',
      panels: ['columns', 'filters', 'views', 'quick'],
      actions: ['undo', 'redo', 'export', 'restore', 'maximise'],
      exportName: 'lattice-demo',
    },
    detail: {
      rows: (row) => row.data.ports,
      height: 260,
      // Bounds what is kept after closing, not how many may be open. Open
      // several and they all stay open; the eleventh closed one is dropped.
      cacheLimit: 10,
      config: {
        rowKey: 'port',
        columns: [
          { field: 'port', title: 'Port', layout: { width: 140 } },
          { field: 'vlan', title: 'VLAN', type: 'number', layout: { width: 100 } },
          { field: 'speed', title: 'Speed', type: 'number', format: { suffix: ' Gb' }, layout: { width: 110 } },
          {
            field: 'state', title: 'State', layout: { width: 130 },
            cell: { decoration: 'dot', variant: { map: { up: 'success', down: 'danger', 'admin down': 'neutral' } } },
          },
          { field: 'peer', title: 'Peer AS' },
        ],
      },
    },
    columns: [
      { field: 'circuit', title: 'Circuit', layout: { width: 160 } },
      { field: 'customer', title: 'Customer', filter: { type: 'set' } },
      { field: 'region', title: 'Region', filter: { type: 'set' } },
      {
        field: 'status', title: 'Status', filter: { type: 'set' }, layout: { width: 140 },
        cell: { decoration: 'pill', variant: { map: { live: 'success', provisioning: 'info', ceased: 'neutral' } } },
      },
      { field: 'portCount', title: 'Ports', type: 'number', layout: { width: 100 } },
      { field: 'charge', title: 'Monthly charge', type: 'number', format: { style: 'currency', currency: 'GBP', decimals: 0 }, total: 'sum', layout: { width: 180 } },
    ],
    rows,  // circuits, each carrying its own ports array
  });
</script>

Nesting a grid inside a row with master-detail

Master-detail expands a row into its own grid rather than a flat sub-list: an order row opens onto its line items, a shipment row onto its parcels, each with its own columns, sorting and totals. A developer reaches for it when the child records do not fit as extra columns on the parent and are too structured for a plain text summary, and when several of those child grids might need to stay open at once for comparison. Lattice Grid controls this per row through the detail: { rows, config } option, passing the child rows and a full grid configuration for the detail, so the nested grid is not a cut-down preview but a complete instance with its own column definitions. Because this is a JavaScript data grid built on incremental rendering, opening a detail row inserts only that row’s grid into the layout rather than recalculating the rows around it, so several master rows can sit expanded together without the outer grid’s scroll position or row heights being recomputed from scratch. The detail area also participates in keyboard navigation, so focus moves from a master row into its expanded grid and back out again without a pointer.

How do I show a nested grid when a row is expanded?

Set detail: { rows, config } on the row: rows supplies the child records and config is a standard grid configuration describing their columns. Lattice Grid renders that configuration inside the expanded row as an independent grid, and any number of master rows can be expanded at the same time, each keeping its own detail state.