Server selection - Enterprise

A grid that keeps a list of selected ids is right until the rows it has seen are a window onto a million on a server. Then "select all" has to mean a million, the header checkbox has to say so, and a bulk edit has to reach rows that never came down the wire. None of that fits a list of ids. It fits a rule - "everything, except these" - which is what the server-side row model keeps when you turn selection on. The demo shows the rule as getSelectionState() reports it, flat and per group, with a bulk edit the server applies by rule.

Open the live example: Server selection: select all, minus these (Server-Side Row Model)

Turning it on

const ctl = createServerRowModel(source, {
  groupBy: ['region', 'country'],
  aggregations: [{ col: 'amount', fn: 'sum' }, { col: 'id', fn: 'count' }],
  getRowId: (r) => String(r.id),
  selection: { groupSelects: 'descendants' },
  grandTotalRow: 'pinnedBottom',
})

The model then supplies the grid's rowSelectionModel through rowModel: the row checkboxes, the header checkbox and api.selectAllRows() all read and write the rule instead of a list. Placeholder rows are skipped; there is nothing there to select.

Two shapes of rule

groupSelects: 'self' (the default) keeps one flat rule:

{ "selectAll": true, "toggled": ["row-17", "row-90"] }

toggled are the exceptions - deselected ids under selectAll: true, selected ids under false. A group row is a row like any other.

groupSelects: 'descendants' keeps a tree of rules, so ticking a group selects everything beneath it and an exception inside a group is recorded under that group:

{
  "selectAllChildren": true,
  "toggled": {
    "EMEA": { "selectAllChildren": false, "group": true,
              "toggled": { "Germany": { "selectAllChildren": true, "group": true, "toggled": {} } } }
  }
}

reads as "everything, except EMEA, except Germany within it". 'filteredDescendants' is accepted as an alias: on a server the filter is already applied, so the rule is the same.

Both shapes are plain data. ctl.getSelectionState() returns the rule and ctl.setSelectionState(rule) restores it, so a selection survives a page reload or travels to an endpoint. Under a descendants rule the state also carries groupBy, the columns its levels are keyed by, which is what a backend needs to resolve it.

An honest count

The selection bar's chip and selection.selectedCount() count what the rule selects, not what is loaded: a million under select-all, a million minus two exceptions, or exactly the ticked rows. Under grouping, the model reads the total from a count aggregation on the grand total row - that is the only place the server says how many leaves there are - and reports null (unknown) without one, which the bar shows as the loaded count.

api.getSelectedRows() stays what it says: the selected rows the grid has loaded. Under select-all that is a window, never the whole set.

A bulk edit by rule

ctl.bulkUpdate(patch) sends the current filter model, the patch and the rule to the datasource's optional updateWhere(filterModel, patch, selection) and resolves with how many rows changed, then reloads every open level. The Enterprise selection bar's Edit fields drawer calls it when the grid runs on a row model, so "edit 1,000,000 rows" is one request carrying one rule.

A datasource without updateWhere refuses the bulk edit rather than quietly writing only the loaded rows. createInMemoryDataSource implements it as the reference, resolving a nested rule level by level; createKitDataSource sends it as one message that createKitHandlers runs through the same authorize, validate, scope and audit hooks as a single-row update. A SQL backend resolves a flat rule as WHERE <filters> AND id NOT IN (<toggled>) under select-all, and a nested one by walking group values the way the grid builds routes.

A new row's selection

A row added by a transaction under a group inherits that group's state: selected under a selected group, not under a deselected one. An indeterminate group passes on its last settled state.

More examples

Server-Side Row Model: 1,000,000 rows

Select-all across a million server-side rows with a bulk edit by rule, on the flagship demo.

Open the live example: Server-Side Row Model: 1,000,000 rows (Server-Side Row Model)

The selection bar

The bar itself, on client-side rows: the count chip, the built-in actions, the bulk-edit drawer and an app's own buttons.

Open the live example: Bulk-action bar (Selection & Clipboard)

Bulk server operations

Select rows, choose an operation, and send one request for all of them - the pattern a bulk edit by rule generalises to rows the grid never loaded.

Open the live example: Bulk server operations (Server-Side Data)

See also

Live examples

  • Server selection: select all, minus these - The header checkbox selects every row the filter matches, loaded or not, and the selection becomes a rule: all except these ids, or per group under grouping. The panel shows getSelectionState() live, Save and Restore round-trip it, and a bulk action sends the rule to the server as one updateWhere that answers with the count it changed. The selection bar shows the server's number, not the ticks on screen.
  • Server-Side Row Model: 1,000,000 rows - One grid, one rowModel prop, a million rows that stay on the server. Sort, filter, global search, grouping to any depth (Region > Country > Rep), infinite scroll or paging, inline edits applied back as transactions with the subtotal following, add and delete, select-all across rows the grid never loaded with a bulk edit by rule, failed blocks with Retry, and a request log that shows every call to the columnar warehouse behind it. The row model ships in @svgrid/enterprise; the datasource contract is free.
  • Bulk-action bar - selectionBar floats a bar over the grid while rows are selected - the count, your actions, an overflow menu, and Clear - the pattern an issue tracker uses for bulk edit. position pins it to the bottom (default) or top edge. Actions receive the selected rows in display order, and hidden / disabled re-check against the live selection so buttons appear and grey out as the set changes.
  • Bulk server operations - Multi-row bulk action with configurable concurrency, live progress bar, per-row outcome chip, mid-flight cancel.

Related articles