Runtime Functions
Most form definitions are static — every shortcut returns a typed object the engine renders once. Runtime functions flip a single widget into reactive mode: pass a callback in place of a value and the engine re-runs it whenever the form data changes, swapping in a new label, a new validator, a new event handler, anything you need.
gui.inputs.textInput('user.name', { label: ({ $form }) => $form.registerMode ? 'Choose a username' : 'Sign-in name',});That single line covers a surprisingly large number of dynamic-form scenarios
without you having to declare a state, write a selector, or split the widget
into two definitions gated by include/exclude.
The callback receives
Section titled “The callback receives”Every runtime function gets the same argument shape:
type DxRuntimeParams<FormData = any> = { $form: FormData; // Live, immutable form data. errors: ValidationStatus | undefined; touched: boolean | undefined; translate: I18nTranslator['translate'] | undefined;};$form is the canonical name — read it instead of the path-bound widget’s
local value. errors and touched reflect the field’s validation state;
translate is the active i18n translator if one is wired up.
What you can reactify
Section titled “What you can reactify”Any prop in a gui.* shortcut can be a function. Below are the shapes you’ll
reach for most.
A reactive label
Section titled “A reactive label”gui.inputs.checkbox('registerMode', { label: ({ $form }) => $form.registerMode ? 'Switch to login' : 'Switch to register',});The label text follows the toggle’s own value — flipping the checkbox swaps the wording immediately.
A reactive validator
Section titled “A reactive validator”gui.inputs.textInput('user.name', { validator: ({ $form }) => $form.registerMode ? { type: 'string', required: true, minLength: 3 } : { type: 'custom', allowedNames: ['John', 'Jane'] },});When the user is registering, the field requires any 3+ character name. When the user is logging in, the field must match one of the allowed names. Same widget, two validation policies, decided per render.
A reactive event handler
Section titled “A reactive event handler”onChange, onLoad, onFilter, and onBlur accept runtime-function
handlers that fire on the event. The handler receives a wrapped
DxFormEvent — same shape as the application-level formEvent
callback, but with an update({ path, prop: value }) helper that
translates into an OVERRIDE_WIDGET_PROP dispatch under the hood. So a
single inline callback can read live form data and push changes to
sibling widgets without leaving the form definition. Pass path to target
an input widget by its data-binding key, or uid to target any widget —
including display widgets (gui.displays.*) that have no data path.
const formDef = [ gui.inputs.booleanInput('upperMode', { label: 'Convert to UPPERCASE' }), gui.inputs.textInput('source', { label: 'Source', placeholder: 'Type something…', onChange: ({ data, update }) => { const text = String(data.source ?? ''); const transformed = data.upperMode ? text.toUpperCase() : text; update({ path: 'mirror', hint: transformed || 'Type in the source field to see it mirrored.', }); }, }), gui.inputs.textInput('mirror', { label: 'Mirror', readonly: true, hint: 'Type in the source field to see it mirrored.', }),];The source field’s onChange reads data.upperMode and data.source,
transforms the value, and pushes it into the mirror field’s hint via
update({ path: 'mirror', hint: ... }). Toggle the uppercase switch to
change the transformation on the next keystroke:
onClick is special: when it’s a function, the function is the
click handler (it receives event.data directly, not the wrapped event)
— so you can’t use it as a runtime selector for a different event name.
For click-driven dispatches into the form, prefer wiring onClick to a
named string event and handling it in your application’s formEvent
callback.
A reactive widget-specific prop
Section titled “A reactive widget-specific prop”Beyond label / validator / event handlers, any prop the widget exposes can be a runtime function — including the widget-specific ones (checkboxPosition, direction, placeholder, hint, etc.). The DX layer flattens those props onto the decorator, so you set them directly without nesting under props:
gui.inputs.checkbox('agreed', { label: 'I agree', checkboxPosition: ({ $form }) => ($form.rtlMode ? 'right' : 'left'),});A reactive display
Section titled “A reactive display”Display widgets that produce a string (alert text, headings, hints) can read
from $form the same way:
gui.displays.alert({ text: ({ $form }) => $form.user?.name ? `Hello ${$form.user.name}!` : 'Welcome — type your name above.', level: 'info',});The renderer widget takes this
to its logical conclusion — its render prop is always a callback, and
returning framework-specific markup (html\…“, JSX, an Angular template) is
the standard usage.
Performance and gotchas
Section titled “Performance and gotchas”- Pure functions only. The engine calls runtime functions on every
relevant data change. Side effects (network requests, timers, mutating
external objects) will fire repeatedly — keep callbacks pure and read from
$form. - Don’t mutate
$form. It’s an immutable view; assigning to it is a no-op but signals a code smell. - Prefer states for shared conditions. If three widgets all branch on
$form.registerMode, define aregisterstate and reference its name from the override map and frominclude. You’ll get a single source of truth that the engine evaluates once.
See also
Section titled “See also”- States — named boolean expressions and gating widgets with
include/exclude/when. - Selectors — decorate widget groups by type, tag, or uid.
- Renderer widget — the canonical example of a function-based prop.