Dialog builder
The Dialog tab of the Screen Designer opens the Visual Builder — a WYSIWYG editor for the add/edit form. The data model is the same ScreenDialog shape stored in screens.toml, but instead of editing TOML you drag fields onto tabs and tune properties in a side inspector.
A screen without a dialog still works as a read-only / grid-edit table. Build a dialog when the user needs a full form to add or edit rows.
Three-column layout
| Region | What it does |
|---|---|
| Palette (left) | Source of new fields. Two sub-tabs: Dictionary (every entry available on the current connector scope) and Columns (every column the read query returns). A search box narrows the list. Click an entry to add a field card on the canvas; drag an entry to drop it at a specific position. |
| Canvas (center) | The dialog's tabs strip across the top + the field grid below. Click a tab to switch; + Add tab adds one. Each field card shows the field name + a one-line summary of its key flags. The selected card has a coloured border; the inspector on the right reflects it. A separate HIDDEN group at the bottom collects fields with hidden = true. |
| Inspector (right) | The properties of the selected field (or a per-tab action editor when a tab action is selected — mutually exclusive). It shows the field and its Column configuration — see Step 3. |
Building from scratch
Step 1 — Open the Dialog tab
The first time, the canvas is empty with a Create dialog button. Click it to seed dialog.tabs = [{id: 'general', label: 'General', fields: []}] and reveal the three-column layout.
Step 2 — Add the first tab's fields
Two ways:
| Method | When |
|---|---|
| Click a Dictionary entry / column in the Palette | Adds a field card at the end of the current tab's grid. |
| Drag a Dictionary entry / column onto the Canvas | Drops the field card where you released it — useful to insert mid-grid. |
The added field becomes the selected card; the Inspector switches to its properties. Set colspan to widen it (the grid is by default 2 columns; a colspan = 2 field fills the row).
Step 3 — Tune the field properties
A dialog field displays a screen column, and the column carries every behaviour. The Inspector shows:
| Section | What it holds |
|---|---|
| Field | The field's name and, with several form tabs, the Tab it sits on. |
| Column configuration | The column's full editor — the same as in the Columns tab: label, format, dictionary entry (dd), rule and lookup binds, required / read-only flags, conditions, defaults, and the field's span on the dialog grid (Display ▸ Colspan). A change applies to the grid as well. |
| Events | The hooks wired on the screen, by event, for information. |
See Columns for each setting and Conditional fields for the conditions.
Step 4 — Add tabs
Click + Add tab above the canvas. The new tab is empty; drag fields in from the Palette. Common tab patterns:
| Tab name | What's on it |
|---|---|
| General | Identity + most-used fields. |
| Audit | Created at, created by, updated at, updated by — often hide_on_add to hide the tab when inserting. |
| Notes | Free-text fields. |
| Address | A group of related fields (street, city, postcode, country). |
Two flags on each tab control visibility per mode:
| Flag | Effect |
|---|---|
hide_on_add | Tab disappears when adding a new row. |
hide_on_edit | Tab disappears when editing an existing row. |
Step 5 — Reorder fields
Drag a field card within the same tab to reorder. Drag across to a different tab to move it between tabs. The drag is HTML5 native — the drop target highlights while you hover. When dragging is awkward, select the field and use the Tab picker in the Inspector to move it to another form tab — the field lands at the end of the target tab and stays selected.
Step 6 — Hide a field
Two ways:
| Method | Effect |
|---|---|
| Tick hidden in the column configuration (Display). | The card moves to the HIDDEN group at the bottom of the canvas. The field still binds at save time. |
| Delete the field from the tab. | The field is removed from the dialog entirely. The column is still on the screen; it just doesn't surface in the form. |
The "hide but keep" pattern is useful for technical columns the dialog must submit (e.g. an audit timestamp) but the user shouldn't see.
The three tab kinds
Tabs aren't all the same. Three types live under dialog.tabs, discriminated on the type field:
| Type | Purpose |
|---|---|
form (default) | A grid of editable fields — the case described above. |
nested_form | The form of another existing screen (form_screen), shown in the tab; param_binds pass the parent's values (usually its key) to that form's queries, and it is saved with the parent's Save. See Nested tabs. |
nested_table | A related-rows TableView embedded in the tab. Renders another screen's grid scoped to the parent's PK. See Nested tabs. |
The visual builder shows the same tab strip for all three — switching to a nested_form or nested_table tab shows the target-screen picker and its param_binds.
Per-tab actions
Each tab can carry its own action list — buttons that fire from inside the tab footer. Common patterns:
| Pattern | Why per-tab |
|---|---|
| A Recompute button on a calculations tab. | Triggers a stored procedure only when the user is looking at those fields. |
| A Test webhook button on a notification tab. | Only meaningful in that context. |
| A Refresh from source button on a sync tab. | Re-pulls data the tab displays. |
The action editor opens in the Inspector when you click an action's row — the Inspector shows one thing at a time (a field OR an action). Selecting a field clears the action selection and vice versa.
See Actions and lifecycle for the full action types.
Dialog title
The dialog's title sits at the top of the modal when it opens. Set it once in the Inspector's Dialog section (below the field properties when no field is selected). Blank → falls back to the screen's label, then to id.
Common pitfalls
| Mistake | Symptom | Fix |
|---|---|---|
| Field on a tab references a column that doesn't exist in the read query. | The dialog shows the field but the value is always blank, and saving sends no value for it. | Either add the column to the read query or remove the field from the tab. |
lookup_param_binds references a field that's not on the same tab. | The bind resolves to NULL at submit. | Either move the source field onto the same tab or use a cross-tab binding (the engine reads the whole form state, but the inspector's Param dropdown only lists fields visible on the current tab). |
colspan = 3 on a 2-column grid. | The field overflows; layout breaks. | Cap colspan at the tab's cols. |
Two fields with the same name on the same tab. | The save sends two values for the same column; the second wins. | Each field's name must be unique within the dialog. |
hide_on_add = true AND hide_on_edit = true. | The tab never appears. | Either flip one off or drop the tab. |
What's next
- Conditional fields — make a field appear / be required / be locked only under specific form conditions.
- Actions and lifecycle — wire on_load / on_save / on_cancel to extend the dialog's behaviour.
- Nested tabs — embed a child-record form or a related-rows grid.