DomainFrameEditor
Defined in: js-api/src/ui/domains/domains-editor.ts:279
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.
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.
Properties
| Property | Modifier | Type | Default value | Description | Defined in |
|---|---|---|---|---|---|
capabilities | readonly | DomainTableCapabilities | undefined | Effective capabilities 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:334 |
client | readonly | DomainTableClient | undefined | The table the frame's rows belong to. | js-api/src/ui/domains/domains-editor.ts:328 |
CHANGES_COLUMN | readonly | "~changes" | '~changes' | JSON column holding the ORIGINAL values of changed cells only (sparse). | js-api/src/ui/domains/domains-editor.ts:283 |
ERRORS_COLUMN | readonly | "~errors" | '~errors' | JSON column holding per-cell DomainCellErrors. | js-api/src/ui/domains/domains-editor.ts:285 |
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:290 |
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:481
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:462
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:695
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:474
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:478
Whether a save is in flight. While it is, the editor refuses every write, discard and refresh — see save.
Returns
boolean
onChanged
Get Signature
get onChanged():
Observable<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:484
Fires on every service-state write — the repaint hook for a grid.
Returns
Observable<DomainFrameEditor>
onConflict
Get Signature
get onConflict():
Observable<DomainVersionConflictError>
Defined in: js-api/src/ui/domains/domains-editor.ts:492
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:486
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:494
Fires with the NEW frame after refresh rebuilt it.
Returns
Observable<DataFrame>
onSaved
Get Signature
get onSaved():
Observable<DomainSaveResult>
Defined in: js-api/src/ui/domains/domains-editor.ts:490
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:488
Fires when isSaving flips — what a grid locks its editing on.
Returns
Observable<boolean>
properties
Get Signature
get properties():
Property[]
Defined in: js-api/src/ui/domains/domains-editor.ts:468
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:471
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:465
'<schema>.<table>'.
Returns
string
Methods
addRow()
addRow(
values?,options?):number
Defined in: js-api/src/ui/domains/domains-editor.ts:587
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
beginEdit()
beginEdit(
row):void
Defined in: js-api/src/ui/domains/domains-editor.ts:533
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.
Parameters
| Parameter | Type |
|---|---|
row | number |
Returns
void
buildOps()
buildOps():
DomainPendingOp[]
Defined in: js-api/src/ui/domains/domains-editor.ts:719
The pending batch as transaction ops, in row order: 'new' rows insert
their writable values, 'modified' rows update ONLY their changed columns
with the row's expectedVersion, 'deleted' rows delete. Exposed so a
caller can inspect or extend the payload (a master-detail save appends its
own ops to one transaction).
An empty cell of a NEW row is LEFT OUT of the insert rather than sent as an explicit null, so the column takes its server-side default; a column with no default and no value is rejected by the server's own nullability check (and by validate before that). Clearing a cell of a MODIFIED row does send null — that is an edit, not an omission.
Returns
changesOf()
changesOf(
row):object
Defined in: js-api/src/ui/domains/domains-editor.ts:507
ORIGINAL values of [row]'s changed cells, keyed by column (empty when the
row is unchanged; always empty for a 'new' row — all of its values are
new). Read-only: the object is the editor's own cached parse, and writing to
it changes nothing on the frame.
Parameters
| Parameter | Type |
|---|---|
row | number |
Returns
object
clearSnapshots()
clearSnapshots():
void
Defined in: js-api/src/ui/domains/domains-editor.ts:550
Drops the beginEdit snapshots. A HOST that rewrites row values IN PLACE (a re-query refreshing the same frame object, with no row-count change and no frame event) MUST call this afterwards: the snapshots are keyed by row index and would otherwise record the OLD rows' values as edit originals (STATE-CONTRACT H8).
Returns
void
commitEdit()
commitEdit(
row,column):void
Defined in: js-api/src/ui/domains/domains-editor.ts:567
Tracks a cell the GRID already wrote (its onCellValueEdited path); the
original comes from the beginEdit snapshot.
Parameters
| Parameter | Type |
|---|---|
row | number |
column | string |
Returns
void
detach()
detach():
void
Defined in: js-api/src/ui/domains/domains-editor.ts:843
Releases the frame subscriptions. The service columns stay on the frame — drop the frame, or remove them, if it outlives the editor.
Returns
void
discard()
discard():
void
Defined in: js-api/src/ui/domains/domains-editor.ts:658
Drops the whole pending batch: changed cells go back to their originals, new rows are removed, deleted rows are restored. Refused while a save is in flight — removing rows under the transaction would make its results land on the wrong ones.
Returns
void
errorOf()
errorOf(
row,column):DomainCellError|null
Defined in: js-api/src/ui/domains/domains-editor.ts:523
The cell's problem, or null.
Parameters
| Parameter | Type |
|---|---|
row | number |
column | string |
Returns
DomainCellError | null
errorsOf()
errorsOf(
row):object
Defined in: js-api/src/ui/domains/domains-editor.ts:513
Per-cell problems of [row], keyed by column. Read-only, see changesOf.
Parameters
| Parameter | Type |
|---|---|
row | number |
Returns
object
isChanged()
isChanged(
row,column):boolean
Defined in: js-api/src/ui/domains/domains-editor.ts:518
Whether the cell carries a pending change (what highlighting keys on).
Parameters
| Parameter | Type |
|---|---|
row | number |
column | string |
Returns
boolean
markDeleted()
markDeleted(
rows):void
Defined in: js-api/src/ui/domains/domains-editor.ts:614
Marks rows deleted: they stay in the frame (order untouched) and are excluded from the filter until save removes them for real.
Parameters
| Parameter | Type |
|---|---|
rows | number | number[] |
Returns
void
refresh()
refresh(
query?):Promise<DataFrame>
Defined in: js-api/src/ui/domains/domains-editor.ts:827
Rebuilds the frame from the server: re-runs [query] (the attached one by default) and re-attaches the service columns from scratch.
Pending edits do NOT survive this — by design. There is no merge: the control is refresh-agnostic and this method never reconciles old and new state. Deciding WHETHER to refresh while edits are pending is the caller's responsibility — check isDirty (or subscribe to onDirtyChanged) and prompt the user to save or discard first.
Resolves to the NEW frame, which also arrives on onRefreshed: a grid bound to the old one must rebind. Refused (resolving to the CURRENT frame, with a warning) while a save is in flight — the resolved value alone does not tell a refusal from a rebuild, so check isSaving first (or compare the frame identity) when it matters.
Parameters
| Parameter | Type |
|---|---|
query? | DomainQuerySpec<string, string> |
Returns
Promise<DataFrame>
revertCell()
revertCell(
row,column):void
Defined in: js-api/src/ui/domains/domains-editor.ts:627
Restores one cell to its original value and drops its change entry. Refused while a save is in flight.
Parameters
| Parameter | Type |
|---|---|
row | number |
column | string |
Returns
void
revertRow()
revertRow(
row):void
Defined in: js-api/src/ui/domains/domains-editor.ts:647
revertCell for every changed cell of [row].
Parameters
| Parameter | Type |
|---|---|
row | number |
Returns
void
save()
save():
Promise<boolean>
Defined in: js-api/src/ui/domains/domains-editor.ts:774
Writes the whole pending batch as ONE /transaction: audit rows share a
tx_id, and any failure rolls every op back. Resolves to whether the batch
landed.
Blocking cell errors refuse the save (naming the first one). A version conflict goes through the platform's standard reload/overwrite dialog and the chosen outcome is applied and retried. Server validation errors land on the offending cells as DomainFrameEditor.ERRORS_COLUMN entries and everything stays pending.
The editor is CLOSED while this runs (isSaving): every write, discard and refresh is refused with a warning instead of being silently lost between the request and its results (and, for the row-removing ones, instead of shifting the rows the results address). A grid bound to the editor locks its own editing off onSavingChanged.
Returns
Promise<boolean>
setValue()
setValue(
row,column,value):void
Defined in: js-api/src/ui/domains/domains-editor.ts:557
Writes [value] into the cell AND tracks it — the programmatic write path (a form field, a paste, a fill-down). Refused while a save is in flight.
Parameters
| Parameter | Type |
|---|---|
row | number |
column | string |
value | any |
Returns
void
stateOf()
stateOf(
row):DomainRowState
Defined in: js-api/src/ui/domains/domains-editor.ts:499
Editing state of [row].
Parameters
| Parameter | Type |
|---|---|
row | number |
Returns
unmarkDeleted()
unmarkDeleted(
rows):void
Defined in: js-api/src/ui/domains/domains-editor.ts:621
Undoes markDeleted, restoring whatever the row was before — a row
added in this batch goes back to 'new', an edited one back to
'modified'.
Parameters
| Parameter | Type |
|---|---|
rows | number | number[] |
Returns
void
validate()
validate():
number
Defined in: js-api/src/ui/domains/domains-editor.ts:684
Re-runs every cell validator over the pending batch; returns the number of
blocking (kind: 'error') cells. Call it before offering Save when values
arrived from outside setValue. Refused (reporting the CURRENT count)
while a save is in flight — it writes the state columns like every
other mutator.
Returns
number
attach()
staticattach(dataFrame,client,options?):Promise<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:349
Attaches the editing state to an EXISTING frame of [client]'s rows (a
queryDf result). Pass options.query so refresh knows what to
re-run.
Parameters
| Parameter | Type |
|---|---|
dataFrame | DataFrame |
client | DomainTableClient |
options? | DomainFrameEditorOptions |
Returns
Promise<DomainFrameEditor>
attachTo()
staticattachTo(dataFrame,schema,table,options?):Promise<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:364
attach for a host that has a frame and a table address but no client of its own — the entry point of the Dart Domain View's grid mode, which owns its frame and hands it over to be edited.
Parameters
| Parameter | Type |
|---|---|
dataFrame | DataFrame |
schema | string |
table | string |
options? | DomainFrameEditorOptions |
Returns
Promise<DomainFrameEditor>
create()
staticcreate(client,options?):Promise<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:372
Runs options.query (everything the caller may see, by default) and
attaches to the resulting frame.
Parameters
| Parameter | Type |
|---|---|
client | DomainTableClient |
options? | DomainFrameEditorOptions |
Returns
Promise<DomainFrameEditor>
editorsOf()
staticeditorsOf(widget):DomainFrameEditor[]
Defined in: js-api/src/ui/domains/domains-editor.ts:455
The editors [widget] answers for, or none — the duck-typed read of IEditorHost a container uses on children of any class.
Parameters
| Parameter | Type |
|---|---|
widget | any |
Returns
DomainFrameEditor[]
forContext()
staticforContext(context,options?):DomainFrameEditor
Defined in: js-api/src/ui/domains/domains-editor.ts:385
SYNCHRONOUS: an editor over an EMPTY frame of [context]'s declared columns — everything attach awaits is already in the prefetched context, so a widget factory can build its editor without a round trip and load the rows afterwards (refresh, which replaces the frame and fires onRefreshed).
Parameters
| Parameter | Type |
|---|---|
context | IDomainTableContext |
options? | DomainFrameEditorOptions |
Returns
DomainFrameEditor
forRows()
staticforRows(client,values,options?):Promise<DomainFrameEditor>
Defined in: js-api/src/ui/domains/domains-editor.ts:408
Builds an editor over rows that do NOT come from the server: a frame is built
locally from the table's declared columns and every entry of [values] is added
as a 'new' row — the INSERT path of a form
(domains.table(...).form({values})), with no query round trip.
The added rows are PRISTINE: an unsaved row the user has not written to yet is not a pending change (see addRow), so an untouched New form has nothing to prompt about.
Everything else is identical to create: the same single-writer model,
the same service columns and export tags, the same validation, and a
save that writes the batch as one transaction. refresh has
nothing to re-run unless options.query says otherwise — and re-running it
would replace these rows, which is why a form never refreshes.
Parameters
| Parameter | Type |
|---|---|
client | DomainTableClient |
values | object[] |
options? | DomainFrameEditorOptions |
Returns
Promise<DomainFrameEditor>
isReferenceProperty()
staticisReferenceProperty(p):boolean
Defined in: js-api/src/ui/domains/domains-editor.ts:448
Whether [p] addresses another row rather than carrying a value of its own — a
ref column (semType '<schema>.<table>') or a user/group column. Those
hold uuids: a picker is their editing path, not a text cell.
Parameters
| Parameter | Type |
|---|---|
p | Property |
Returns
boolean
validateCellValue()
staticvalidateCellValue(p,value):string|null
Defined in: js-api/src/ui/domains/domains-editor.ts:427
Validates one cell value against its registry Property — the same constraints the server re-runs on write, producing the server's exact message texts so an inline marker and a rejected save read identically.
Numeric properties take a dart2js-safe path (under dart2js every whole number
is double, which would reject every integer), and string_list values are
left to the server, which coerces them. Returns null when the value is fine.
Parameters
| Parameter | Type |
|---|---|
p | Property |
value | any |
Returns
string | null