methods — expression methods and the .df whitelist (generated)
$[method::(args)]only knows the 46 methods below. An unknown method name does not error — it silently evaluates to empty (the unit stays on the widget, the number vanishes), so check here before writing one. The argument count is a minimum; extra arguments are ignored. Usage lives innumable docs dfandnumable docs rcn.
Methods
| Method | Arguments (min count) | Returns | Notes |
|---|---|---|---|
calc |
expression string(1) | number | null | Arithmetic and parentheses. Android returns a double, so an integer renders as 17.0; wrap a number bound for text in round::. Any size string containing calc must be wrapped again in findNotEmpty::, otherwise a null result leaves a bare pt and the whole widget fails. Leading zeros are read as decimal (07 is 7, not octal) on iPhone, Android, HarmonyOS and desktop alike, so there is no need to run the value through parseNumber:: first; surrounding spaces are trimmed too. Only decimal is accepted: a form like 0x10 cannot be read and the whole expression comes back empty |
urlEncode |
string(1) | string | Always run a dynamic segment through this before splicing it into a URL |
urlDecode |
string(1) | string | It turns + into a space before decoding |
base64Encode |
string(1) | string | |
base64Decode |
string(1) | string | |
get |
object, path(2) | any | Reads a value out of an object by path, and the path may itself be a variable: $[get::(${resp.rates},${q})] is the right way to say "take the entry the user picked". A ${} inside a method argument can only see keys already landed in the flow (an action's id result, or a key from op:set) — it cannot see depends inputs; land an input with op:set first, then use it |
if |
condition, value if true, value if false(3) | any | All three arguments are required; do not feed the number it returns straight into calc::, land it first |
eq |
value A, value B(2) | boolean | If both sides convert to numbers they are compared as numbers: eq::("00","") and eq::(empty,0) are both true. Always test for emptiness with the sentinel $[if::(eq::(findNotEmpty::(${x},none),none),0,1)] (not length::, which returns 0 for any number) |
ne |
value A, value B(2) | boolean | Same numeric comparison rule as eq:: |
gt |
value A, value B(2) | boolean | Always false if either side is not a number |
lt |
value A, value B(2) | boolean | Always false if either side is not a number |
ge |
value A, value B(2) | boolean | Always false if either side is not a number |
le |
value A, value B(2) | boolean | Always false if either side is not a number |
or |
condition… (any number)(1) | boolean | Takes every argument |
and |
condition… (any number)(1) | boolean | Takes every argument |
floor |
number(1) | number | null | Android returns a double; wrap in round:: before putting it in text |
round |
number(1) | number | null | Android returns a double |
ceil |
number(1) | number | null | Android returns a double |
min |
number… (any number)(1) | number | null | Android returns a double |
max |
number… (any number)(1) | number | null | Android returns a double |
parseNumber |
value, pattern(2) | string | null | Pattern grammar: 0 = integer (truncate toward zero); 0.00 = exactly two decimals; 0.## = up to two; a , in the integer part (e.g. #,##0.00) adds thousands separators. Format numbers before they reach the widget; large values need grouping or a smaller font or they overflow the widget edge |
formatDate |
timestamp, format string(2) | string | null | Tokens are yyyy MM dd HH mm ss. An empty string renders as a fake 1970 time, so wrap it in a findNotEmpty:: sentinel. Multiply a second-precision timestamp by 1000 first. ⚠️ It formats in the device's own time zone (the same on iPhone, Android, HarmonyOS and desktop): one timestamp renders differently on machines in different zones, which is usually what you want ("what time is it here") |
parseDate |
text, format string(2) | number | null | The only cross-platform-safe format is plain tokens plus - : / and spaces; letters (such as T or Z) make the whole parse fail on iOS/Android, so cut the string clean with subString:: first. ⚠️ It parses in the device's own time zone: most APIs hand you UTC (2026-09-08T12:00:00Z), and feeding that in straight makes it be read as local time — the timestamp comes out a whole time zone off, with no error. For a UTC string, either subtract the offset first or take an epoch timestamp from the source instead |
random |
none(0) | number | A random number between 0 and 1; do not use it on a widget, it makes the cache and the renders disagree |
md5_32 |
string(1) | string | 32-character lowercase digest |
md5_16 |
string(1) | string | 16-character digest |
length |
string | array | object(1) | number | It returns 0 for a number, so gt::(length::(a number),0) cannot answer "is there a value" — land an explicit flag in the flow instead; length::(number) also still differs across platforms, so only use it as a failure test on string fields |
replaceAll |
source, regex, replacement(3) | string | The second argument is read as a regular expression, so a pipe and similar symbols cannot be used as separators — use an ordinary character such as ~ |
subString |
source, start, end(3) | string | Cuts by code point, and clamps to the bounds when they are exceeded |
toLowerCase |
string(1) | string | |
toUpperCase |
string(1) | string | |
endsWith |
source, suffix(2) | boolean | |
matches |
source, regex(2) | boolean | An invalid regular expression returns false rather than raising an error |
startsWith |
source, prefix(2) | boolean | |
echo |
any value(0) | the value unchanged | Returns its input as is, commonly used to land the result of an expression into a key |
connect |
fragment… (any number)(1) | string | It eats the spaces at both ends of every argument, and on Android a number joins as 17.0. To keep spaces, or to mix text and values, interpolate with ${} inside the txt text instead |
split |
source, regex(2) | string[] | The second argument is read as a regular expression; do not use a pipe as the separator or the whole string is split character by character; trailing empty strings are dropped from the result |
index |
string | array | object, index(2) | element | null | Takes an element by index: a string by code point, an array by position, an object by insertion position; a negative index returns null. To read an object by key use get::, not index:: (passing a string key silently yields empty). Nested subscripts like ${a[${i}]} are not supported — writing one silently yields empty and overwrites the default parameter |
contains |
source, substring(2) | boolean | A plain substring test, not a regular expression |
findNotEmpty |
candidate… (any number)(0) | the first non-empty value | The workhorse fallback: any size string containing calc:: must be wrapped in it, and time anchors and colors should fall back to a literal through it too, or an empty value makes the block disappear or the whole widget fail to render |
sum |
number… (any number) | array(1) | number | Returns 0 when there are no numbers. An array must be written as a real array literal, not stuffed into a method argument |
pluck |
array, path (optional)(1) | array | Picks one column out of an array of objects; anything that is not an array comes back unchanged |
join |
array, separator (optional)(1) | string | The separator defaults to an empty string |
mapField |
array, source column, lookup table, new column, default (optional)(4) | array | null | Returns a new array (the input is untouched): each object row is shallow-copied and gets new column = table[row[source column]], or the default when there is no match (empty string if no default is given); non-object elements are kept as-is; a first argument that is not an array gives empty. Lookup keys: strings as-is (not trimmed), integers without .0, null / missing as an empty string, booleans as true / false |
groupSum |
array, key column(s) (array or one column name), value column, weight column (optional)(3) | object | null | Groups by the key column(s) and sums value × weight; several key columns give nested objects whose leaves are numbers. Values and weights are trimmed and parsed as decimals; anything unparseable counts as 0. Results are rounded with Math.round(x × 1e6) / 1e6. An empty array gives {}; a non-array, an empty key array or an empty key string gives empty; an empty weight column counts as not given. Store the key-column array in a variable with op:set first and pass that — never write commas inside the argument. Key order is not guaranteed |
convertSum |
amount table, rate table, target currency (optional)(2) | number | null | Σ amount[currency] / rate[currency], counting the target currency itself as 1; currencies with a 0 amount are skipped. If any currency has no rate (or a rate ≤ 0) the whole result is empty — better to show -- than to undercount. An empty or all-zero amount table gives 0. The result is rounded with Math.round(x × 1e6) / 1e6. The amount table is usually a groupSum grouped by currency |
Global rules
- An unregistered method silently evaluates to empty:The method table is exactly the list above: a misspelled name, or a method that is not in the table (abs::, avg::, filter::, groupBy:: (to sum by column use groupSum), indexOf::, push:: and the like), raises no error — it just evaluates to empty and that spot silently vanishes from the widget. For absolute value write if::(ge::(${v},0),${v},calc::(0-${v}))
- Evaluation order is $[…] → ${…} → @[…]:Methods run first, then data variables are resolved, and finally any @[…] left in the resulting string is resolved as an in-package asset reference
- Nested methods are written as bare names:Inside a $[…], call another method as name::(...) — do not open a second $[; a nested $[ makes the whole widget fail to render
- All four platforms provide @app / @env / @device / @time:You can read built-in variables such as ${@app.platform} directly; there is no need to pass them yourself
What may appear in a .df
A data flow accepts only the items below; anything else (especially ui.* / nav.* / toast) is rejected at packaging time.
| Category | Allowed |
|---|---|
| action | cancel error finish sleep request clearCookie htmlParse xmlParse log resultfilter resultFilter data.get data.set data.remove data.has data.keys data.getAll data.merge data.clear credential.state |
operation (op key) |
if for forEach set remove include |
composite (in the action slot) |
sequential concurrent |
| explicitly forbidden | showLoading hideLoading toast plus every App-injected action (ui.* nav.* xpage.* widget.* singleValue startPageForResult installBundle) |