Views
A *.view.xml file describes one object — a grid, an editable form, or a read-only view.
Whether it behaves as one, the other, or all three is inferred from which SQL blocks it declares;
there is no separate “table view” vs “form view” type. Internally this is the unified ObjectView.
The root is <Table> or <Form> (both parse to one ObjectView; the filename is *.view.xml
regardless).
<Table Name="Customer" Slug="customers" Label="Customers" Icon="fa fa-users" FormStyle="Modal"> <Sql PrimaryKey="Id" SortBy="Name" SortDirection="ASC" PageSize="20"> … </Sql> …</Table>Root attributes
Section titled “Root attributes”| Attribute | Notes |
|---|---|
Name (required) |
the view name (used in /table/{name} etc.) |
Slug (required) |
URL-friendly identifier for the view. Always kebab-case (lowercase, words separated by hyphens), e.g. customers, sale-items, user-roles. The router and ViewResolver match a route by either Name or Slug, so the slug is the readable form used in /table/{slug}, /form/{slug}, /view/{slug}. |
Label, Icon |
display label and icon class |
FormStyle |
Inline | Modal | DirectedForm |
TableDisplay |
Default | Block | Flex |
DisableActions, DisableFilters, DisableControls, DisableRelativeTime, LoadAsScalar |
feature toggles |
CreateForm, EditForm |
delegate create/edit to another named view |
Breadcrumb parent (<ParentView>)
Section titled “Breadcrumb parent (<ParentView>)”By default the breadcrumb roots a form/view at the table it was opened from (the runtime origin) or, failing that, at the view’s own name. For an orphan form — one reached via a deep link or a different module, with no originating grid — that produces a self-referential or wrong root crumb.
Declare a logical parent with an optional <ParentView> element. Its Name maps to a navbar
item’s Name; the UI resolves that item’s label and route and uses it as the breadcrumb root.
When present it overrides the runtime origin; when omitted the default behaviour is unchanged.
<Form Name="IncidentForm" Slug="incident-form" Label="Incident"> <ParentView Name="incident" /> <!-- breadcrumb: Home › Incident › Edit --> <Sql> … </Sql> …</Form>If the name matches no navbar item, the breadcrumb falls back to a /table/{name} link with a
humanized label. A single parent is supported (not a multi-level chain). See
Navbar for how item Names are declared.
SQL blocks
Section titled “SQL blocks”Capabilities are inferred from which blocks are present — a view with only <Sql> is a read grid; add
<InsertSql>/<EditSql>/<DeleteSql> (or <SubmitSql>) and it gains a form.
| Element | Role |
|---|---|
<Sql PrimaryKey SortBy SortDirection PageSize> |
required primary query. PageSize defaults to 10; SortDirection is ASC/DESC |
<With> |
optional raw CTE prelude for the primary <Sql> only |
<InsertSql> / <EditSql> / <DeleteSql> |
table-style writes (use @Param per field; @Id for the PK) |
<SubmitSql> |
form-style single submit (instead of Insert/Edit/Delete) |
<ScriptBlock> |
server-side script block |
The write blocks can drive the client via dispatch callbacks
(return a result set whose first column is FunctionName).
<With> — CTE prelude
Section titled “<With> — CTE prelude”<With> is prefixed to the primary <Sql> at execution time. When <With> is absent, the
primary <Sql> runs exactly as authored. The block may include the leading with keyword, or may
start directly with the CTE list; Genie normalizes it to one leading WITH before passing the
composed SQL to the existing query builder.
<With> with active_customers as ( SELECT Id, Region FROM Sales.Customers WHERE IsDeleted = 0 ), regional_totals as ( SELECT CustomerId, SUM(Total) AS TotalAmount FROM Sales.Orders GROUP BY CustomerId )</With><Sql PrimaryKey="Id" SortBy="Id"> SELECT c.Id, c.Region, t.TotalAmount FROM active_customers c JOIN regional_totals t ON t.CustomerId = c.Id</Sql>The runtime SQL is the raw With prelude followed by the existing primary select. The table query
builder owns the outer wrapping, filters, search, sorting, count, schema, and paging: it hoists the
WITH prelude above the SELECT * FROM ( … ) subquery wrapper (a CTE can’t live inside a derived
table), so the CTE is in scope for the wrapped query, count, and schema probe alike. One or more CTEs
are supported. <With> does not apply to <InsertSql>, <EditSql>, <DeleteSql>, <SubmitSql>, row
actions, import SQL, or select-field datasets. Any parameters referenced in <With> or <Sql> must
still be allowed by the existing parameter/editor-field/pre-execute-filter rules (session params such
as @SessionCompanyId are available inside the prelude). Worked example (multiple CTEs):
sample/Inventory/models/views/CategoryStockReport.view.xml.
Parameters
Section titled “Parameters”<Parameters> <Parameter Name="Id" Required="false" /></Parameters><Parameters> declares query parameters — the allowlist ParameterSanitizer honours, alongside
editor-field names. Every @parameter a query references must actually be supplied; referencing an
unsupplied parameter raises “Must declare the scalar variable @X”.
Anatomy of a view
Section titled “Anatomy of a view”A full <Table>/<Form> composes these building blocks, each covered on its own page:
- Editor fields & datasets —
<EditorFields>(the create/edit form). - Layout —
<Layout>(how the form is arranged). - Column & field expressions —
<Columns>(grid columns), styling, and the smart field rules. - Actions & row actions —
<Actions>,<RowActions>,<Filters>, toolbar quick-search. - Bulk import & handlers —
<ImportConfig>. - Sub-views —
<SubConfig>(embedded child grids). - CartTable — the inline line-item editor.
Worked example (kitchen-sink): sample/Inventory/models/views/Products.view.xml.