Skip to main content

Create a screen from a query

You have a <base>_get query (and ideally its _put / _post / _delete siblings) — the CRUD Wizard is the fastest way to land them. Now you wire a screen on top so users can see and edit the rows.

This page walks the General and Queries tabs of the Screen Designer. Columns, dialog, actions and hooks come in the next pages.


Step 1 — Register the app, if needed

On Settings → Screens, the scope bar lists every app (= connector) that already has at least one screen. If the connector you want to add a screen to isn't there:

  1. Click + Add screens for a connector.
  2. Pick the connector from the dropdown (only connectors without a screens namespace are listed — once registered, the connector becomes an app chip).
  3. The chip appears at the top; the screen list below is empty.

If the connector is already a chip, just click it.


Step 2 — Add a screen

Below the empty list, click the + Add screen button. The page prompts for a screen id — letters, digits, underscores, leading letter. Conventions:

PatternExample
Match the table base.customers for a screen on customers_get.
Match the JD Edwards object.F0005 for the JDE UDC table.
<entity>_<purpose> for screens that don't map 1-1 to a table.customer_balance_report, invoice_send_dialog.

The id is stable — it's the URL segment (/screen/<app>/<id>), the dictionary key ([screens.<app>.<id>]) and the cross-file reference target. Renaming later goes through the Rename button on the card, which propagates the change across screens / menus / dictionary.

The empty screen appears in the list; click it to open the Screen Designer.


Step 3 — The General tab

The designer opens on General. This tab is where you wire identity, the connector override and the behaviour flags.

Screen Designer · crm.customers · GeneralConnectorUse the app's connector — crm ▾LabelCustomersDescriptionPortfolio customers — read-writeauto_loadrun the read query as soon as the screen openseditableallow inline cell editing in the griduploadableshow the Excel / CSV import buttonaudit_tablee.g. AUD_USERS — leave blank to disablemax_rows(connector default)key_columnsCUSTOMER_IDinitial_group_byREGIONWHEN A ROW IS CLICKEDBehaviorDo nothing (or fall through to the screen's own dialog) ▾Other options: Open a sibling Screen as a modal dialog · Open a page route in a new browser tab

Field by field

FieldNotes
ConnectorDefaults to Use the app's connector — <app>. Set explicitly only when the screen reads/writes through a different connector than the one the app is named after (rare but legitimate — e.g. a crm app that reads its reporting tables through a reporting connector).
LabelShort text shown in menus and lists. Falls back to description, then id.
DescriptionLonger text shown as the page title.
auto_loadWhen on, the grid runs the read query as soon as the screen opens. When off, the user clicks Run to fetch. Turn off only for screens with required filter parameters or genuinely expensive queries.
editableWhen on, the grid supports inline cell editing — click a cell, type, press Enter; Save commits via update_query. When off, the user edits only through the dialog.
uploadableWhen on, an Excel / CSV Import button appears in the grid toolbar. Requires key_columns to know which rows are updates vs inserts.
audit_tableSet to a table name (convention: AUD_<TABLE>) to mirror every successful write into it. Each row gets AUD_ACTION (INSERT/UPDATE/DELETE), AUD_USER (the caller's username), AUD_DATE (server timestamp). Blank = no audit.
max_rowsCap on the read query's result. Blank uses the connector's / pool's default (typically 1000).
key_columnsColumns that uniquely identify a row — drives the Excel-import update-vs-insert match and the dialog's edit-mode lock. Leave blank to derive from columns whose key flag is on (the recommended path — see Columns).
initial_group_byOne or more column names — the grid groups by these on first open. The user can ungroup / regroup from the Group control.
treeviewSet when the rows form a parent/child hierarchy — e.g. menu trees, organisation charts. Adds a Tree view toggle alongside Table / Chart. See Concepts → Screens for the full reference.
chart_idA saved chart id (from [charts.*]) — pre-fills the Chart view toggle. Blank uses a session-local default.

When a row is clicked

The dropdown picks one of three behaviours:

ModeWhat happens
Do nothing (or fall through to the screen's own dialog)Default. If the screen has its own dialog, clicking a row opens it in edit mode. Otherwise the click is a no-op.
Open a sibling Screen as a modal dialogThe row's columns bind into another screen's read query (you pick the target + the binds). That sibling screen's dialog opens as a modal. Useful for master-detail without duplicating dialog markup.
Open a page route in a new browser tabA SPA route — escape hatch for hand-written React pages. Use {column_name} placeholders to interpolate the row's columns (URL-encoded). Example: /nomaflow/runs/{id}.

When both route and Open sibling Screen are set, route wins — the explicit route is the more specific intent.


Step 4 — The Queries tab

Switch to Queries. This is where the screen learns which queries to fire on read and on each write.

Screen Designer · crm.customers · QueriesRead query *customers_get ▾Update querycustomers_put ▾Insert querycustomers_post ▾Delete querycustomers_delete ▾The ✎ button opens the query in its own editor — saves there commit through to connectors.toml without leaving this designer.
FieldRequiredNotes
Read queryYesThe SELECT that fills the grid. The dropdown is fed from the selected connector's queries list.
Update queryNoThe writable query fired by the dialog's Save in edit mode and the grid's Save button. Convention: <base>_put.
Insert queryNoThe writable query fired by the dialog's Save in add mode. Convention: <base>_post.
Delete queryNoThe writable query fired by the dialog's Delete button (or the grid's per-row delete). Convention: <base>_delete.

The pencil icon (✎) on each row opens the query in a side modal — quick edits to a query don't require leaving the Screen Designer. The modal saves through to connectors.toml directly; reload happens automatically.

Picking from the CRUD wizard's output

If you generated the four queries via the CRUD Wizard, they're already in the dropdown — customers_get, customers_put, customers_post, customers_delete. Wire them in order and the screen has full CRUD.

For read-only screens (reports, dashboards, audit views) leave the three write fields blank. The grid still works; the dialog (if any) is read-only; the + Add / 🗑 Delete buttons disappear.


Step 5 — Save and see the result

Click Save in the modal header. The screen is persisted, the hot reload fires, and:

  • A new menu entry for this screen can be added (see the upcoming Menus section).
  • The screen is reachable directly at /screen/<app>/<id>.
  • A user with sql:<connector>:<read_query> permission sees the grid.

Common pitfalls at this stage

MistakeSymptomFix
read_query unset.Save fails validation.Pick one — the read query is the only required query.
update_query set but key_columns empty AND no key flag on any column.The dialog's Save in edit mode runs the UPDATE without a :NAME_ORIGINAL clause and updates the wrong row.Either set key_columns or mark the key columns in the Columns tab.
editable = true but no update_query.The grid shows the cell editor but the Save button fails.Wire the update_query.
uploadable = true but no key_columns.The importer can't tell update from insert.Set key_columns.
Connector override pointing at a pool the user has no permission on.Sign-in users get an empty grid with no obvious error.Either change the override or grant the user the right sql: permission.
Same screen id reused across apps.The cross-link screen:<app>:<id> is per-app, so collisions across apps are fine. But within the same app, the validator refuses duplicates.Pick unique ids per app.

What's next

  • Columns — configure each column once for both the grid and the dialog.
  • Dialog builder — the visual designer for the add/edit form.
  • Actions and lifecycle — toolbar buttons, row menus, on_load / on_save / on_insert hooks.