CnObjectDataWidget
Schema-driven editable data grid widget. Displays object properties in a CSS grid, supports inline editing (click-to-edit with all widget types), dirty tracking, and saves via objectStore.
Usage
<CnObjectDataWidget
title="Character info"
:schema="schema"
:object-data="character"
object-type="characters" />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | String | 'Data' | Widget title in the card header |
icon | Object|Function | null | Optional MDI icon component for the header |
object-data | Object | required | The object to display and edit. Keys must match the schema property keys. |
schema | Object | required | JSON Schema defining properties. Must have a properties field. |
object-type | String | '' | Registered object type slug in the objectStore. Required for saving via objectStore.saveObject(). |
store | Object | null | Optional objectStore instance. When provided, used directly for saving instead of auto-detecting via Pinia. |
overrides | Object | {} | Per-property configuration overrides (see below) |
columns | Number | 3 | Number of grid columns |
editable | Boolean | true | Whether editing is enabled globally — gates both inline (click-to-edit) and the full-form Edit action item |
edit-label | String | 'Edit' | Label for the Edit action item, which opens a schema-driven CnFormDialog pre-filled with the object (alongside inline editing) |
exclude | Array | [] | Property keys to hide from display |
include | Array | null | Property keys to show (whitelist — all others hidden) |
save-label | String | 'Save' | Label for the save button |
discard-label | String | 'Discard' | Label for the discard button |
empty-label | String | 'No data available' | Label when no properties are found |
documentation-url | String | '' | Documentation link surfaced in the widget's overflow Actions menu (empty hides the Documentation item). |
widget-id | String | '' | Stable id forwarded to the widget chrome (falls back to object-type). |
metadata-label | String | 'Metadata' | Label for the Metadata item in the overflow Actions menu. |
Slots
| Slot | Scoped props | Description |
|---|---|---|
#actions | — | Extra buttons in the widget header (right side, next to save/discard) |
#field-{key} | { field, value, update, cancel } | Override the inline editor for a specific property |
#display-{key} | { field, value, raw } | Override the display (read-only) view for a specific property |
Events
| Event | Payload | Description |
|---|---|---|
@saved | result object | Emitted after a successful objectStore save |
@save-error | error message | Emitted when the objectStore save fails |
@save | merged data object | Emitted when no objectType is set — lets the parent handle the save |
@discard | — | Emitted when the user clicks the discard button |
One config, two surfaces (display + edit modal)
The overrides / exclude / include props drive a single fieldsFromSchema pipeline that both the inline display and the full-form Edit modal (CnFormDialog) consume. So a property hidden via overrides.id.hidden = true is hidden in the widget grid and dropped from the edit form; an order set on a property reorders both. gridColumn/gridRow (span) are display-only; the modal is single-column (stacked).
fieldsFromSchema honors, per property: hidden (drop the field), order (wins over the schema's own order for sorting), readOnly: false (un-skip a schema-readonly field), plus any field props to merge (label, widget, enum, …).
Configuring it in-app
On a detail page in OpenBuild edit mode, the widget's cog opens CnObjectDataWidgetForm: it lists the schema's properties (resolved from the widget's register/schema, or the page's injected cnObjectContext when the widget inherits them) and lets you, per property, toggle visibility, set a label, span, editor type and editable, drag to reorder, and pick a layout preset (Stacked / 2-col / 3-col → the columns value). The form emits a minimal overrides map persisted on the widget's content.overrides.
Property overrides
The overrides prop accepts per-property configuration:
{
propertyKey: {
order: 1, // Sort order (lower = first)
gridColumn: 2, // Number of grid columns to span
gridRow: 2, // Number of grid rows to span
hidden: false, // Whether to hide this property
editable: true, // Whether this property can be edited
label: 'Custom', // Override the display label
widget: 'textarea', // Override the widget type for editing
enum: [...], // Override enum values for select/multiselect
}
}
Supported widget types
The widget auto-detects the editor based on the JSON Schema property type:
| Widget | Schema type | Editor |
|---|---|---|
text | string | Text input |
email | string format email | Email input |
url | string format uri | URL input |
number | number/integer | Number input |
textarea | string (long) | Textarea |
select | string with enum | Single select dropdown |
multiselect | array with enum | Multi-select dropdown |
tags | array (no enum) | Tag input |
checkbox | boolean | Toggle switch |
date | string format date | Date picker |
datetime | string format date-time | Datetime picker |
Example with overrides
<CnObjectDataWidget
title="Publication details"
:schema="publicationSchema"
:object-data="publication"
object-type="publications"
:overrides="{
title: { order: 1, gridColumn: 2 },
description: { order: 2, gridColumn: 2, widget: 'textarea' },
status: { order: 3, editable: false },
internalNotes: { hidden: true },
}" />
Conditional immutability (x-openregister-readonly-when)
A schema property can declare that it becomes read-only when another field on
the same object holds a given value — the widget evaluates the rule against the
live object data and renders the field locked (shown, not hidden). This is the
declarative way to express identity fields that must not be edited in a
particular state (e.g. a hybrid app's slug/name/description/
productionVersion, which mirror the installed app it customizes).
// In the OpenRegister schema property:
"slug": {
"type": "string",
"x-openregister-readonly-when": { "field": "appType", "equals": "hybrid" }
}
// or match a set of values:
"slug": { "type": "string", "x-openregister-readonly-when": { "field": "appType", "in": ["hybrid", "managed"] } }
An unconditional "readOnly": true on the property locks the field in every
state (still shown, never editable). Both are honoured by isEditable; a
per-field overrides[key].editable still takes priority. Read-only here is a UI
affordance — pair it with server-side enforcement (an OpenRegister write guard /
listener) for the authoritative boundary.