Skip to content

Column & field expressions

Genie ships an eval-free expression engine (Genie SQL-flavoured) that powers three things: grid column display/styling, editor field rules (required / disabled / hidden / security), and computed values. 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 and required-ness are enforced, not just hinted).

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.

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

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')" />
<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), Style, StyleClasses, Hide, Export, RolesAllowed (CSV), RelativeTime, Search, IsAttachment, StyleClassesExpression, Lookup, LookupKey, LookupParam.

  • Hide — when true, 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, and Lookup links) and it is listed in the column manager (“Columns” toolbar button) unchecked, so a user can re-enable it. This replaces the legacy Style="display:none" hack. Use it for key/id columns backing a Lookup or for secondary columns most users don’t need. Defaults to false.
  • Expression — an optional client-side display expression (Genie SQL-flavored, the same engine as StyleClassesExpression and 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 via StyleClasses / StyleClassesExpression.
  • IsAttachment — when true, 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 to false.
  • 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 after StyleClasses — e.g. If(Status = 'Overdue', 'gx-danger', 'gx-success').
  • 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 both Lookup (a target view name) and LookupKey (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 typically Hide-d — its value still ships to the client for the link. LookupParam names the parameter the key is passed under (defaults to Id). The dialog has a View button that navigates to the full /view/{Lookup}?{LookupParam}={key} 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. Keep LookupParam as Id for the View button to round-trip (the /view page reads Id).
<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.xmlStyleClassesExpression badges, Lookup links, RelativeTime, IsAttachment, RolesAllowed, plus smart Required/Hidden expressions, Allow/Deny field security, and a ValueExpression margin.