Core Guarantees

What every surface — the table, the views, the inspector, the api — promises about your records, reads, writes, refusals and history.

Wit Grid is one record engine behind many surfaces: the table, the board, the calendar, the Gantt, the gallery, charts, pivots, the inspector and your own code through the api. This page states what holds wherever a record is read or written. If a surface behaves differently from what is written here, that is a bug.

Your records

  • You own the records. The grid holds them by id (getRowId); an id must stay the same for the life of a record.
  • Client rows come from rows and change through transactions. Server and infinite rows come from your datasource; the grid holds the ones it has loaded.
  • Views show the records that match, not what the table happens to display: the filters, the search and the sort apply to every view; the table's collapsed groups, its page and its pivot do not. A view over a server or infinite model knows it sees only what is loaded (completeness: 'loaded') and says so.

Reading a field

  • A field reads the same everywhere. Every surface reads it through the grid: a valueGetter, a formula, a nested path (plan.amount) give the same value in a table cell, on a card, in a chart total, in a formula that uses it, in a filter's value list, in a diff and in a pivot.

Writing a field

Every interactive cell write — an edit, a paste, a fill, a transaction, a card moved, a span dragged, a field changed in the inspector — is prepared the same way, then committed:

  1. Setters run once, on a copy. Each written field's valueSetter runs against a copy of the record as proposed so far. A setter that returns a promise is awaited by async callers (edits, pastes, gestures, transaction(…, { async: true })); a sync transaction refuses it (needsAsync: true) instead of leaving it unawaited. Nothing runs twice.
  2. Only what changed is written. The commit writes the fields a setter actually changed (by value), plus the plain writes as given. A field the setter left alone is not rewritten.
  3. The rules judge the whole proposed record. Cell rules run for every field the write changes, including fields a setter changed besides its own; row rules run against the record with every change in place. A blocking rule that throws blocks the write.
  4. Permissions apply to people. Edits, pastes, fills and gestures are checked against your capabilities; your own code's transaction is not, unless it asks (asUser: true).
  5. All or nothing, by default. A write is atomic: if one cell cannot apply, none do. A bulk action can choose policy: 'bestEffort', where independent cells apply and the refused ones are listed.
  6. Prepared against the record as it commits. Writes queued before it land first; if the record changed while a setter or rule was awaited, the write is prepared again, and refused (“changed while saving”) if it changes again — it never commits over values it did not see.

A committed write is one change set (the changed event, listing exactly the fields that changed) and one undo step.

Refusals

  • A refused write changes nothing (or, with bestEffort, nothing but the cells that could apply).
  • It is reported the same way from every surface: the write's result says why (validationFailed, capabilityDenied, rejected), and the writeBlocked event fires with its source — edit, paste, fill, or view for a gesture. Trying to edit a cell the user may not is reported too.
  • The user is told why, in your words. In a workspace, a refused edit and a refused gesture both read “Not changed: …” with your rule's message or your capability's reason. An editor keeps its draft.
  • Nothing waits forever. A gesture still awaiting a setter reports itself as pending, and is reported when it settles; a write still waiting when the grid is destroyed settles as refused.

History

  • Undo reverses one write: exactly the fields it changed. A later change to other fields of the same record — a collaborator's, the server's — is left as it is.

Limits, stated

  • A rule judges the proposed record it is given. What it reads through the api — this record or any other — is the committed state, not the rest of the same write. A rule across records does not see the other records proposed in the same transaction.
  • Row additions and replacements (rows.add, rows.update) are validated after they commit, not before; only cell writes are prepared and checked first.
  • Changes from your server or another device are authoritative: they are applied as given, without your setters or rules.
  • Workflow rules belong to the record, not the view. A board's WIP prompt or a Gantt's impact preview is the view's gesture help; a rule that must hold everywhere is a validation rule, a capability or a value setter — see how the landing page's CRM declares its sales rules once and every view keeps them.

What a view never does

The built-in views are projections over the same record engine. A view never reads a field from the record's data itself, never runs a setter, and never writes a cell except through the workspace's one write — so every guarantee above holds in it. A test in the core enforces this.

Edit on GitHub

Last updated on

On this page