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:
| Property | Expected value | Example |
|---|---|---|
listen | A fully prefixed watched path | $value.customer.id |
field, from, to | A literal form-control path | customer.id |
when, set.value | A JEXL expression | ${$value.customer.id != null} |
scriptData, runEffect.inputs | Values or objects containing JEXL expressions | {"id": "${$value.customer.id}"} |
effectId | A literal effect ID | process-customer |
objectPointer | A 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. For an array, a legacy one-level effect
may use items.$row.target. An effect with scope: "groups!.lines" uses
groups!.lines!.target instead; each ! is bound to a concrete array 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 discoveryReading $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
$trigger is available in tSM UI 2.4 builds that include TSM-4228. Older 2.4
deployments do not evaluate or suggest this variable.
Effect expressions use the same JEXL runtime as computed form properties, with an effect-specific scope:
| Value | Meaning in a Form Effect |
|---|---|
$value | Current value of the complete root form. |
$context | Form context from the Fluent Forms store. |
$outputContext | Output context from composed widgets and flows. |
$config | Application/API configuration. |
$configUi | Reserved for UI configuration. The current Form Effects runtime supplies {}, so do not depend on it yet. |
$runtimeInfo | Current user and runtime metadata. |
$version | Application version. |
$build | Build metadata. |
$row | Concrete row index for a row-scoped value effect or array widget event; 0 when no row scope is present. |
$item | Current row value in an effect with scope; for example, $item.source in when or value. |
$parents | Ancestor {row, item} entries for a scoped effect, nearest first. $parents[0].row is the immediately enclosing array's row index. |
dataSources | Registered datasource functions. |
dataSourcesValues | Settled datasource values available to the evaluator. |
$dataSourcesLoading | Whether an expression datasource is still resolving. |
$event | Complete semantic event in a widget-event effect. |
$payload | Shorthand for $event.payload. |
$input | Evaluated input object in an invoked effect. |
$response | Script result in the script's direct outcome branch. |
$trigger | Transient cause of the current effect run, including the immediate change origin and the initiator of the complete effect chain. |
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.
For nested arrays, listen watches a $value variable path such as
$value.groups!.lines!.source. When scope names the matching array, !
binds its row positions in listen and literal action paths. Use $item,
$row, and $parents inside JEXL expressions. See the
nested-array example.
Use $trigger.origin when an effect must react only to a direct user edit:
$trigger.origin == 'user'
Effect actions set the next run's immediate origin to effect. To include
later effect-written values from the same user-started cascade, test
$trigger.initiator == 'user' instead. Other useful metadata includes
$trigger.type (valueChange, widgetEvent, invoked, or initialization)
and $trigger.changes, which lists the relevant changed paths and their
origins.
$trigger is available only while an effect is executing; it is not form data
and is not available in ordinary widget expressions. See the
complete $trigger attribute reference
for type, origin, initiator, individual changes, chainId, and
parentEffectId.
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 mode | Runtime behavior |
|---|---|
plainValue | Resolve a scalar or object value on the first emission. |
identifiedPack | Wait until the returned pack has loading !== true. Read the result from .data. |
unsupported | Reject 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.