Skip to main content
Version: 2.5

Paths and Expressions

Choose the right path syntax

Form Effects use several similar-looking values with different syntax. This is the most important distinction to remember:

PropertyExpected valueExample
listenA fully prefixed watched path$value.customer.id
field, from, toA literal form-control pathcustomer.id
when, set.valueA JEXL expression${$value.customer.id != null}
scriptData, runEffect.inputsValues or objects containing JEXL expressions{"id": "${$value.customer.id}"}
effectIdA literal effect IDprocess-customer
objectPointerA JSON Pointer to a widget in the schema/layout/0/items/2

Control paths use dot notation. Do not add $value, ${...}, or JSON Pointer slashes to field, from, or to. The only dynamic segment supported in an action path is the fluent-array row placeholder $row.

// Correct
{"type": "set", "field": "customer.email", "value": "${$value.contact.email}"}

// Incorrect: field is not a JEXL expression
{"type": "set", "field": "${$value.customer.email}", "value": "${$value.contact.email}"}
listen is the trigger, not automatic dependency discovery

Reading $value.customer.id in when, set.value, or scriptData does not automatically make the effect react to that field. Add every form value whose change should rerun the effect to listen. The designer warns when it detects a $value dependency that is not covered by listen.

Legacy listen entries that wrap a simple path in ${...} still work, but new effects should use a direct prefixed path.

Expression Context

Effect expressions use the same JEXL runtime as computed form properties, with an effect-specific scope:

ValueMeaning in a Form Effect
$valueCurrent value of the complete root form.
$contextForm context from the Fluent Forms store.
$outputContextOutput context from composed widgets and flows.
$configApplication/API configuration.
$configUiReserved for UI configuration. The current Form Effects runtime supplies {}, so do not depend on it yet.
$runtimeInfoCurrent user and runtime metadata.
$versionApplication version.
$buildBuild metadata.
$rowConcrete row index for a row-scoped value effect or array widget event; 0 when no row scope is present.
dataSourcesRegistered datasource functions.
dataSourcesValuesSettled datasource values available to the evaluator.
$dataSourcesLoadingWhether an expression datasource is still resolving.
$eventComplete semantic event in a widget-event effect.
$payloadShorthand for $event.payload.
$inputEvaluated input object in an invoked effect.
$responseScript result in the script's direct outcome branch.

The stage-specific values are preserved in a script outcome branch belonging to that same effect. An invoked effect receives only the values explicitly mapped into $input.

Member access

Author ordinary dot and bracket access in Form Effects expressions:

$value.customer.address.street
$response.items[0].name

The expression compiler adds safe member access. Do not add JavaScript optional access operators such as ?. to persisted Form Effects expressions. Use a fallback when the final result can be missing:

$value.customer.address.street || 'N/A'

See the JEXL language reference for operators, functions, transforms, and Public API data sources.

Datasources

Only datasources declared for effect usage can be called from an effect expression. The current runtime resolves datasource calls in when, set.value, and runEffect.inputs; it does not resolve them in scriptData:

Effect modeRuntime behavior
plainValueResolve a scalar or object value on the first emission.
identifiedPackWait until the returned pack has loading !== true. Read the result from .data.
unsupportedReject the datasource in an effect and omit it from effect autocomplete.

Example using an identifiedPack datasource:

{
"id": "load-user-name",
"listen": ["$value.userId"],
"do": [
{
"type": "set",
"field": "userName",
"value": "${evalScriptByCode('GetUserName').data}"
}
]
}

Datasource parameters are reevaluated when their dependencies change. A new value-change run supersedes an older synchronous run that has not completed.