Skip to content

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>
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

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.

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> 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>
<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”.

A full <Table>/<Form> composes these building blocks, each covered on its own page:

Worked example (kitchen-sink): sample/Inventory/models/views/Products.view.xml.