Column & field expressions
Genie ships an eval-free expression engine (Genie SQL-flavoured) that powers four things: grid column display/styling, editor field rules (required / disabled / hidden / security), computed values, and validation rules. The same grammar runs on the UI (live, as values change) and on the server (the engine ships a C# port of the interpreter, so field-level security, required-ness and validation are enforced, not just hinted).
The expression grammar
Section titled “The expression grammar”Expressions are SQL-flavoured predicates evaluated against the other field values (or, for grid
columns, the row’s cell values). Reference a field/column by bare name or @Name;
AND/OR/NOT, LIKE, IN, arithmetic, and If(cond, a, b) are supported. Row-action visibility
expressions reference the row as row.Column (see Actions & row actions).
Smart field attributes — Required / Disabled / Hidden
Section titled “Smart field attributes — Required / Disabled / Hidden”Required / Disabled / Hidden on any editor field are “smart” attributes — each holds
either a literal ("true"/"false") or a row-context expression (the merged successors of the
old RequiredExpression/DisabledExpression/HiddenExpression, which no longer exist):
<Text Name="Reason" Required="@Status == 'Reject'" /> <!-- required only when rejecting --><Number Name="Price" Disabled="@Type == 'B'" /> <!-- read-only when Type is B -->Required is re-validated server-side on submit — not just enforced in the UI. For value rules beyond required-ness (patterns, bounds, and author-declared assertions), see Validation.
Field-level security — Allow / Deny
Section titled “Field-level security — Allow / Deny”Allow / Deny whitelist / blacklist who may edit a field. Each holds either a comma-separated
role list or a row-context expression; default is editable by all. A field is editable iff
(Allow empty OR allowMatches) AND NOT (Deny matches). A locked field renders read-only in the UI
and — crucially — is enforced server-side on submit: a denied field’s caller-supplied value is
discarded (create → DefaultValue/null, edit → the previous stored value is kept), so tampering with
the raw API cannot change it.
<Number Name="CreditLimit" Allow="Admin,Manager" /> <!-- only these roles may edit --><Text Name="Notes" Deny="@Status == 'Closed'" /> <!-- locked once the row is closed -->Computed values — ValueExpression
Section titled “Computed values — ValueExpression”ValueExpression computes the field’s value and re-runs whenever its inputs change (make the
field Disabled="true" for a read-only computed display). Beyond field references + arithmetic, the
helper sumLines(cart, 'QtyColumn', 'PriceColumn') totals a <CartTable>’s
line items:
<Number Name="TotalAmount" Label="Total" Disabled="true" ValueExpression="sumLines(LineCart, 'Quantity', 'UnitPrice')" />The price key is optional: sumLines(cart, 'Column') sums that one column — the natural fit when
each line already carries a computed amount:
<Number Name="Subtotal" Label="Subtotal" Disabled="true" ValueExpression="sumLines(Lines, 'LineAmount')" />ValueExpression also works on a <CartTable> column,
where its scope is the row’s own cells — e.g. a per-line LineTotal of Quantity * UnitPrice.
Grid columns — <Columns>
Section titled “Grid columns — <Columns>”<Columns> <Column Name="Id" Hide="true" /> <Column Name="Name" Search="true" /> <Column Name="CreditLimit" Label="Credit" StyleClasses="text-end" Export="false" /> <Column Name="RegisteredOn" Label="Registered" RelativeTime="true" /></Columns>Per result column: Label, Expression (or element body), StyleClasses,
StyleClassesExpression, Render, Hide, Export, RolesAllowed (CSV), RelativeTime,
Search, IsAttachment, Lookup, LookupKey, LookupParam.
Hide— whentrue, the column is hidden by default in the grid, but it still ships to the client (its cell values remain available for row identity, actions, andLookuplinks) and it is listed in the column manager (“Columns” toolbar button) unchecked, so a user can re-enable it. This replaces the legacyStyle="display:none"hack. Use it for key/id columns backing aLookupor for secondary columns most users don’t need. Defaults tofalse.Expression— an optional client-side display expression (Genie SQL-flavored, the same engine asStyleClassesExpressionand field rules) evaluated per row by the UI against the row’s cell values; its result becomes the cell’s displayed text. It is never evaluated server-side — any computation that needs SQL (joins, aggregates, CASE/IIF, badge markup) belongs in the DataSet query instead, and styling/badges are applied client-side viaStyleClasses/StyleClassesExpression.IsAttachment— whentrue, the column’s value is treated as a set of attachment file paths (pipe/comma-separated) and the UI renders the cell as attachment tags (image thumbnails + file pills) instead of plain text. Defaults tofalse.StyleClassesExpression— an optional client-side expression (Genie SQL-flavored, same engine as field rules) evaluated per row against the row’s cell values; its string result is appended to the cell’s CSS classes afterStyleClasses— e.g.If(Status = 'Overdue', 'gx-danger', 'gx-success').Render— render the cell value as rich content instead of plain text, selected by name. Built-ins:badge(one coloured chip),tags(a pipe/comma-separated value → a row of chips),bullets(→ a bulleted list),progress(a numeric0–100value → a progress bar with a percentage label). Any other value resolves to a host-registered renderer (see Frontend integration › Rich cell renderers), falling back to plain text when unknown. Colour/label come fromStyleClasses/StyleClassesExpression— e.g.Render="badge"withStyleClassesExpression="If(Status = 'Overdue', 'gx-danger', 'gx-success')". Renderers produce React nodes (the value is auto-escaped) — no HTML is ever authored in the model or the value. The same attribute is available on a view’s<EditorField>to render its read-only value.Search— marks the column for the toolbar quick-search box (see Actions & row actions).Lookup/LookupKey/LookupParam— turn the column value into a lookup link. When bothLookup(a target view name) andLookupKey(a sibling column supplying the primary key) are set and the row’s key value is non-empty, the cell renders as a clickable link that opens the linked record read-only in a dialog (the target view’s full layout). The key column is typicallyHide-d — its value still ships to the client for the link.LookupParamnames the parameter the key is passed under (defaults toId). The dialog has a View button that navigates to the full/object/{Lookup}?id={key}record page. Loading the linked record is gated by the View permission on the target view; if the user lacks it the dialog opens and shows the server error. KeepLookupParamasIdfor the View button to round-trip (the record page reads the pk from the reservedidquery param). The same link appears in a record view wherever the column is shown as a read-only field (a<ViewLayout>item, or a band’s identity, subtitle or summary). The key column need not be in the layout, because the record’s values carry every column of the form Sql.
<Columns> <Column Name="CustomerId" Hide="true" /> <Column Name="CustomerName" Label="Customer" Lookup="Customers" LookupKey="CustomerId" /></Columns>Row-action visibility — DisplayExpression
Section titled “Row-action visibility — DisplayExpression”A row <Action> uses DisplayExpression (or DisplayColumn) for conditional visibility — the same
expression engine, evaluated per row against row.Column. See
Actions & row actions.
Worked example: sample/Inventory/models/views/Products.view.xml — Render badges/tags/progress
(plus a host-registered code renderer on Sku), StyleClassesExpression colours, Lookup links,
RelativeTime, IsAttachment, RolesAllowed, plus smart Required/Hidden expressions,
Allow/Deny field security, and a ValueExpression margin.