Actions & row actions
A view can carry toolbar actions (buttons above the grid), row actions (per-row buttons or menu items), a quick-search box, and pre-execute filters. This page covers all four, plus the CSP-safe dispatch envelope that lets server SQL drive the client.
Toolbar actions & row actions
Section titled “Toolbar actions & row actions”<Actions> <Action Name="ExportCsv" Icon="fa fa-file-csv" ExportHandler="Csv">exportTable('Customer');</Action></Actions>
<RowActions> <Action Name="Deactivate" Icon="fa fa-ban" Color="warning" Confirm="Sure?" DisplayExpression="row.IsActive == true"> <Sql>UPDATE Sales.Customers SET IsActive = 0 WHERE Id = @Id;</Sql> </Action> <Action Name="AdjustCredit" Icon="fa fa-sliders"> <Sql>UPDATE Sales.Customers SET CreditLimit = @NewLimit WHERE Id = @Id;</Sql> <Prompt Message="New credit limit" ConfirmLabel="Apply"> <Number Name="NewLimit" Label="New Credit Limit" Required="true" MinValue="0" /> </Prompt> </Action> <Action Name="Site" Icon="fa fa-globe" Url="{Website}" Target="_blank" DisplayColumn="Website" /></RowActions>A toolbar <Action> carries an OnClick script (element body), optional Icon, Confirm,
ExportHandler. A row <Action> resolves its body by precedence <Sql> > Url (link) >
OnClick/script, plus Name, Icon, Color, Confirm, Target, Permission,
DisplayExpression/DisplayColumn (conditional visibility — see
expressions), and an
optional <Prompt> (Message, ConfirmLabel + editor fields collected before the action runs).
Link actions
Section titled “Link actions”A Url makes the action a link. {Column} tokens are substituted from the row ({Id} included), and
tokens landing in the query string are percent-encoded, so a value containing &, # or = can’t
corrupt the parameters after it.
Target decides where it opens:
Target |
Behaviour |
|---|---|
omitted / _self |
An internal path navigates in-app (SPA route); an external URL opens in the same tab. |
_blank (or any anchor target) |
Opens a window. Note a new tab carries no bearer token, so don’t point it at an [Authorize] endpoint. |
Modal |
Opens the document viewer in a dialog, without leaving the grid. Only applies to a /report-view or /file-view URL; anything else falls back to navigation. Superseded by Type="Report" below, which does the same by default. |
Document actions — Type="Report"
Section titled “Document actions — Type="Report"”Type="Report" is the preferred way to open a report or stored file, and it opens in a dialog by
default. It is the one Type an author may declare: Sql, Link and Script are inferred from the
action’s body, so naming them is rejected rather than silently ignored. A Report still needs a Url —
the viewer URL it opens.
Mode then decides where, and is only valid alongside Type="Report":
Mode |
Behaviour |
|---|---|
omitted / Dialog |
The default. Opens the document viewer in a dialog over the grid, keeping the list, its scroll position and its pre-execute filters underneath. Right for a document that is glanced at and dismissed. |
Redirected |
Opens the viewer in a new tab with the sidebar minified, for a document somebody sits and reads. Safe despite the _blank warning above, because the tab loads the SPA viewer page — not the [Authorize] endpoint — and same-origin tabs share the bearer token. |
A Report whose Url is not a viewer path has no dialog to open, so it falls back to navigation
rather than producing a dead button.
<!-- Opens the document dialog — Mode defaults to Dialog --><Action Name="Invoice" Icon="fa fa-file-pdf" Type="Report" Url="/report-view?report=sales.invoice&title=Invoice&invoiceId={Id}" />
<!-- Same document, in its own tab with the sidebar minified --><Action Name="InvoiceTab" Icon="fa fa-up-right-from-square" Type="Report" Mode="Redirected" Url="/report-view?report=sales.invoice&title=Invoice&invoiceId={Id}" />
<!-- A stored file (images preview; every other type downloads) --><Action Name="Contract" Icon="fa fa-file" Type="Report" Url="/file-view?path={ContractDocument}&title=Contract" DisplayColumn="ContractDocument" />
<!-- Plain link: no Type, so it navigates to the viewer page in place --><Action Name="InvoicePage" Icon="fa fa-file-pdf" Url="/report-view?report=sales.invoice&title=Invoice&invoiceId={Id}" />Target="Modal" predates Type="Report" and still works, so existing views need no edit — but prefer
Type="Report" in new ones: it says what the action is rather than overloading the anchor target, and
it is what Mode attaches to.
Dispatch callbacks
Section titled “Dispatch callbacks”A <Sql> block (in a row action, submit, delete, import AfterSql, or an RPC) can drive an action
instead of just returning a value. Return a result set whose first column is named FunctionName;
each row becomes one callback, with the remaining columns as positional params (Param1, Param2, …
in column order, SQL NULL → null). Names must match ^[A-Za-z_][A-Za-z0-9_]*$, and rows run in
sequence, each awaited, so a callback completes before the next row runs.
Callbacks come in two kinds, and the split matters:
- Server callbacks — starting or advancing a workflow. The engine runs these itself, inside the request that produced them, and strips them from the response. They never reach the browser, so backend operations aren’t exposed as public endpoints.
- UI callbacks — everything in the table below. These are returned to the React client, which runs
each row through a closed registry — no
eval, no inline JS.
A single result set can mix them; the server rows execute, the rest are handed on:
SELECT 'startWorkflow' FunctionName, 'OrderApproval', 'Orders', @Id -- runs on the serverUNION ALLSELECT 'showSuccess', 'Submitted', 'Sent for approval.', NULL; -- runs in the browser-- Validation message that keeps the form/grid open:SELECT 'showAlert' FunctionName, 'error', 'Invalid Work Package Dates', 'Each work package must stay within project start/end dates.';
-- Refresh the grid after a successful update:UPDATE … ; SELECT 'refreshTable' FunctionName;Supported UI FunctionName values and their positional params (the server callbacks are documented
in Workflow callbacks):
FunctionName |
Params | Effect |
|---|---|---|
refreshTable |
— | Reload the surrounding grid/record |
showAlert |
severity, title, message | Blocking dialog; severity = error/success/info/warning |
showSuccess |
title, message | Blocking success dialog |
showError |
title, message | Blocking error dialog |
swal |
title, message, severity | Legacy alias for showAlert (severity defaults to error) |
closeModal |
— | Dismiss the surrounding modal |
navigate |
url | Same-origin redirect (internal routes navigate in-app; javascript: refused) |
historyBack |
— | Browser back |
openWindow |
url, target, features | window.open (defaults _blank, noopener,noreferrer; javascript: refused) |
The client registry lives in src/ui/genie-engine-ui/src/components/genieActions.ts; it is wired into
every mutation handler so any <Sql> that returns this shape is dispatched automatically. An unknown
FunctionName reaching the client surfaces an “Unknown action” dialog rather than failing silently.
Toolbar quick-search
Section titled “Toolbar quick-search”Marking one or more columns Search="true"
enables a search box in the table toolbar. On submit (Enter or the Search button) the typed term is
applied server-side as a single LIKE '%term%' predicate OR-gated across all searchable columns,
then AND-combined with any active column filters — e.g.
(Name LIKE @t OR Email LIKE @t) AND Status = @s. The box appears only when the view declares at least
one searchable column; the term itself is always parameterized and the searchable set is derived from
the view config server-side (the client never picks the columns). SQL-backed views only.
Filters (pre-execute)
Section titled “Filters (pre-execute)”<Filters Toggle="Collapsed"> <!-- Active | Collapsed --> <Select Name="CustomerType" Label="Type"><DataSet Type="static"> … </DataSet></Select> <Date Name="RegisteredFrom" Label="Registered from" /></Filters>Filter fields become @parameters available to <Sql> (guard with (@Param IS NULL OR …)). Children
are editor fields (or <Filter Name="" Type="" /> shorthand); an optional <Layout> lays them out.
Filter <Select>s use the same dataset system as
editor fields. A DateRange filter arrives as two parameters, @{Name}From and @{Name}To.
Worked example: sample/Inventory/models/views/Products.view.xml — toolbar CSV export, row actions
with <Sql> + a <Prompt> + refreshTable/showAlert dispatch, quick-search columns, and
model/sql/static <Filters>.