Skip to content

Inline cell editing

Grid cells are aggregates, so the editable surface is the source rows behind them: expand a leaf to its master-detail table and click a source cell to edit it. On commit the engine re-derives every affected aggregate — the frontend does zero math, it only reports the change. The new value is coerced to the column’s dtype first, so a typed column never gets silently corrupted.

# The detail cell emits a wire edit; a callback applies it through the engine and returns
# refreshed row_data. old_value is an optimistic stale guard (compared in wire terms).
result = engine.apply_edit(
["region", "country"],
{"path": ["EMEA", "Germany"], "row_index": 0,
"column": "qty", "old_value": 3, "new_value": "5"}, # "5" widens to the int column
)
# result is the refreshed source row; rebuild the payload to push new aggregates to the grid.
  • Click-to-edit. With masterDetail + editable, source cells in the detail panel swap to an input on click; Enter or blur commits, Escape cancels. Grid cells stay read-only — they are aggregates, not a write surface.
  • Addressed by path + row index. An edit is { path, rowIndex, column, oldValue, newValue } — the rowIndex-th source row matching the group-key path, exactly like drill-through. oldValue is an optimistic stale guard: if the underlying cell changed since the editor read it, the edit is dropped.
  • Immediate recompute. The adapters run core applyEdit over an internal data overlay and rebuild the tree; Dash round-trips through engine.apply_edit. Either way every subtotal, grand total, and calculated measure that depended on the cell is recomputed by the engine.

The new value is coerced to the column’s dtype before the frame mutates — this is what keeps a sum a number and a temporal column temporal:

  • Numeric widening. A numeric column accepts only real numbers; a "5" typed into an int/float column is parsed to a number (JS Number(text) / Python numeric coercion). A non-numeric string is rejected rather than stringifying the whole column and breaking every aggregation.
  • Temporal parsing. A wire ISO string edited into a Date / Datetime / Time column is parsed back to that temporal dtype (str.to_date / to_datetime / to_time); an unparseable value is rejected as incompatible.
  • Rejections surface via onEditRejected ({ code, column, rule?, message }) and restore the source cell. Add per-column validationRules (range / required / regex / predicate) for business rules on top of the dtype guard — see source-row editing for the full validation reference.