DomainFrameEditor
Defined in: js-api/src/ui/domains/domains-editor.ts:310
THE single writer of a domain frame's editing state.
It wraps a DataFrame produced by table.queryDf(...) and attaches three
invisible service columns — DomainFrameEditor.STATE_COLUMN,
DomainFrameEditor.CHANGES_COLUMN, DomainFrameEditor.ERRORS_COLUMN —
that hold everything about the pending batch: which
rows are new/modified/deleted, the ORIGINAL value of every changed cell, and
the per-cell validation errors. Grids, forms and the save pipeline all read
that one state; nothing keeps a parallel store.
const editor = await DomainFrameEditor.create(grok.dapi.domains.table('grit.issue'));
editor.setValue(0, 'title', 'New title'); // tracked, validated, highlighted
await editor.save(); // ONE /transaction
Every service column is tagged out of binary AND csv export, so the state is
memory-only: a saved project, toByteArray(), toCsv(), an export or a
batch() upload built from the frame never carry it.
Writing. Go through setValue (programmatic) or beginEdit + commitEdit (an in-grid edit, where the grid has already written the cell). Writing a cell directly on the DataFrame bypasses the tracking and the value is silently NOT saved.
Deleted rows stay in the frame and are hidden by ANDing them out of the filter bitset on every filter recomputation, so undoing a delete (unmarkDeleted) is trivial and row order never moves. The mirror case — a row the server already deleted, staged to come back by markRestored — stays VISIBLE for the same reason: it is a pending change the user must see.
Refreshing discards edits — BY DESIGN. refresh re-runs the query and rebuilds the frame and its state from scratch; there is no merge and never will be. Deciding whether it is safe to refresh is the CALLER's job: read isDirty / subscribe to onDirtyChanged and prompt (save / discard / cancel) before calling it. A component that refreshes on a timer or on a route change without that check WILL eat a user's batch edits.
Implements
Properties
| Property | Modifier | Type | Default value | Description | Defined in |
|---|---|---|---|---|---|
access | readonly | DomainAccess | undefined | Effective access of the current user, SNAPSHOT when the editor was created — what read-only degradation and the writable-column payload filter derive from. A later grok.dapi.domains.invalidateUiCaches() (or a grant change) does NOT reach an existing editor: re-create it to pick the new permissions up. | js-api/src/ui/domains/domains-editor.ts:398 |
client | readonly | DomainTableClient | undefined | The table the frame's rows belong to. | js-api/src/ui/domains/domains-editor.ts:392 |
quiet | readonly | boolean | undefined | See DomainFrameEditorOptions.quiet. | js-api/src/ui/domains/domains-editor.ts:379 |
CHANGES_COLUMN | readonly | "~changes" | '~changes' | JSON column holding the ORIGINAL values of changed cells only (sparse). | js-api/src/ui/domains/domains-editor.ts:314 |
DRAFT_ID_PREFIX | readonly | "~new:" | '~new:' | Prefix of the id addRow stamps into a row that does not exist on the server yet: ~new:<uuid>. Another row (of this or of another editor in the same DomainSession) may hold it in a ref column — buildOps turns it into the transaction's $ref and the server resolves it. | js-api/src/ui/domains/domains-editor.ts:328 |
ERRORS_COLUMN | readonly | "~errors" | '~errors' | JSON column holding per-cell DomainCellErrors. | js-api/src/ui/domains/domains-editor.ts:316 |
LIVE_ROWS | readonly | RegExp | undefined | The referential refusal the server sends back, which names the child table and the column pointing here (DomainRepository._checkDeletable) — restrictRefusal says it in the user's words. | js-api/src/ui/domains/domains-editor.ts:333 |
SERVICE_COLUMNS | readonly | readonly string[] | undefined | The three service columns an editor attaches — every one of them tagged out of binary AND csv export, so the editing state can never reach a saved project, an export, an upload, or a batch() fed from the frame. | js-api/src/ui/domains/domains-editor.ts:321 |
STATE_COLUMN | readonly | "~state" | '~state' | Row state column: `'' | 'new' |
Accessors
changeCount
Get Signature
get changeCount():
number
Defined in: js-api/src/ui/domains/domains-editor.ts:595
Number of pending cell changes — what a "N unsaved changes" bar shows.
Returns
number
dataFrame
Get Signature
get dataFrame():
DataFrame
Defined in: js-api/src/ui/domains/domains-editor.ts:576
The frame being edited. It is REPLACED by refresh — re-read it (or subscribe to onRefreshed) instead of caching it.
Returns
errorCount
Get Signature
get errorCount():
number
Defined in: js-api/src/ui/domains/domains-editor.ts:839
Number of cells whose problem blocks save.
Returns
number
isDirty
Get Signature
get isDirty():
boolean
Defined in: js-api/src/ui/domains/domains-editor.ts:588
Whether anything is pending (a changed cell, a new row, a deleted row).
Returns
boolean
isSaving
Get Signature
get isSaving():
boolean
Defined in: js-api/src/ui/domains/domains-editor.ts:592
Whether a save is in flight. While it is, the editor refuses every write, discard and refresh — see save.
Returns
boolean
Implementation of
onChanged
Get Signature
get onChanged():
Observable<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:598
Fires on every service-state write — the repaint hook for a grid.
Returns
Observable<DomainFrameEditor>
Implementation of
onConflict
Get Signature
get onConflict():
Observable<DomainVersionConflictError>
Defined in: js-api/src/ui/domains/domains-editor.ts:606
Fires when a save hits a version conflict, BEFORE the standard dialog.
Returns
Observable<DomainVersionConflictError>
onDirtyChanged
Get Signature
get onDirtyChanged():
Observable<boolean>
Defined in: js-api/src/ui/domains/domains-editor.ts:600
Fires when isDirty flips — what a caller's refresh policy listens to.
Returns
Observable<boolean>
onRefreshed
Get Signature
get onRefreshed():
Observable<DataFrame>
Defined in: js-api/src/ui/domains/domains-editor.ts:608
Fires with the NEW frame after refresh rebuilt it.
Returns
Observable<DataFrame>
Fires with the NEW frame when the editor rebuilt it — the grid rebinds.
Implementation of
onRefused
Get Signature
get onRefused():
Observable<DomainEditRefusal>
Defined in: js-api/src/ui/domains/domains-editor.ts:611
Fires when the HOST refused an edit (refuse) — what a status line shows next to the grid's balloon.
Returns
Observable<DomainEditRefusal>
onSaved
Get Signature
get onSaved():
Observable<DomainSaveResult>
Defined in: js-api/src/ui/domains/domains-editor.ts:604
Fires after a successful save.
Returns
Observable<DomainSaveResult>
onSavingChanged
Get Signature
get onSavingChanged():
Observable<boolean>
Defined in: js-api/src/ui/domains/domains-editor.ts:602
Fires when isSaving flips — what a grid locks its editing on.
Returns
Observable<boolean>
Implementation of
properties
Get Signature
get properties():
Property[]
Defined in: js-api/src/ui/domains/domains-editor.ts:582
Registry Property metadata of the table's declared columns.
Returns
Property[]
query
Get Signature
get query():
DomainQuerySpec<string,string> |undefined
Defined in: js-api/src/ui/domains/domains-editor.ts:585
The query refresh re-runs.
Returns
DomainQuerySpec<string, string> | undefined
table
Get Signature
get table():
string
Defined in: js-api/src/ui/domains/domains-editor.ts:579
'<schema>.<table>'.
Returns
string
writableColumns
Get Signature
get writableColumns():
string[] |null
Defined in: js-api/src/ui/domains/domains-editor.ts:421
IFrameEditor: null when NOTHING in the frame can be edited — no
table-level edit or insert, and no per-row ~can_edit to override them —
otherwise the columns the field rights let anyone write, canEdit
deciding the ROW dimension per cell. A row-mode frame answers the list while
the table-level edit is false, or the whole grid would lock.
Returns
string[] | null
The columns the user may edit; null (or empty) when NOTHING in the frame can be edited — the whole grid is read-only.
Implementation of
Methods
addRow()
addRow(
values?,options?):number
Defined in: js-api/src/ui/domains/domains-editor.ts:704
Appends a new, unsaved row (state 'new'), optionally prefilled; returns
its index (-1 when refused because a save is in flight).
options.pristine adds it as a row nobody has written to YET: it is part of
the batch a save writes, but it contributes NOTHING to
changeCount / isDirty until the first setValue or
commitEdit — the "pristine until touched" contract of an insert form,
whose untouched (however prefilled) row must not arm the unsaved-changes gate.
A row added by a USER gesture (the grid's Add row) is pending immediately,
which is the default.
Parameters
| Parameter | Type |
|---|---|
values? | {[column: string]: any; } |
options? | { pristine?: boolean; } |
options.pristine? | boolean |
Returns
number
applyResults()
applyResults(
pending,results,assigned?):Promise<DomainSaveResult>
Defined in: js-api/src/ui/domains/domains-editor.ts:988
Lands this editor's slice of a successful transaction: returned ids and versions into the frame, every cell holding a draft id the transaction resolved (this editor's, or another participant's through [assigned] — the DomainSession collects the whole batch's map before the first slice is applied), then every OTHER server-assigned column of the rows it wrote (writeBack), row state cleared, deleted rows removed.
onSaved fires LAST, once the re-read has landed: a host that rebuilds
on it (a form's system footer, a list) must see the server's rows — the
transaction answers {id, version, created} per insert and nothing else, so
number, created_on, updated_on and author_id exist only after it.
Parameters
| Parameter | Type |
|---|---|
pending | DomainPendingOp[] |
results | any[] |
assigned? | {[draftId: string]: string; } |
Returns
Promise<DomainSaveResult>
beginEdit()
beginEdit(
row):void
Defined in: js-api/src/ui/domains/domains-editor.ts:650
Snapshots [row]'s current values so a following commitEdit knows what the cell held BEFORE the edit. A grid calls this when the cell becomes current — an edit can only start there. Without a snapshot the edit is still tracked and saved, it just cannot be reverted.