Products · Developer guides
Expressions
Compare, calculate and choose values inside $(...) with operators and built-in functions.
Anything inside $(...) that is more than a plain reference is an expression. Expressions use the Apache Commons JEXL syntax. They only compute a value: they can't loop, assign or change anything.
$(rc.display_name) a plain reference
$(page.enter_amount.value * 2) a number
$(page.enter_amount.value >= 100) true or false
$(strings.upper(rc.display_name)) text
$(rc.is_member == 'true' ? 'Welcome back' : 'Join today')A $(...) is a plain reference when it starts with page., session., data., rc. or sc. and has only letters, digits, dots and underscores. Anything else is read as an expression. For where you can use $(...), see Variables.
Operators
| Kind | Operators | Example |
|---|---|---|
| Arithmetic | +, -, *, /, % | $(page.ticket_count.value * 50) |
| Comparison | ==, !=, <, <=, >, >= | $(page.enter_amount.value >= 100) |
| Logic | &&, ||, ! | $(rc.is_member == 'true' && !strings.is_blank(sc.chosen_term)) |
| Choice | ? : | $(rc.tier == 'gold' ? 'Gold' : 'Standard') |
Default for null | ?? | $(rc.display_name ?? 'friend') |
| Pattern match | =~, !~ | $(page.phone.value =~ '232[0-9]{8}') |
| Join text | + | $('Hi ' + rc.display_name) |
=~ checks the whole value against the pattern, not part of it. To check how a value starts, use strings.has_prefix.
Built-in Functions
Numbers
| Function | Returns | Example |
|---|---|---|
max(a, b) | The larger number | $(max(page.enter_amount.value * 1, 10)) |
min(a, b) | The smaller number | $(min(page.enter_amount.value * 1, 10000)) |
abs(a) | The number without its sign | $(abs(sc.balance_change * 1)) |
These take numbers. Values read with $(...) are text, so multiply them by 1 first, as above.
strings
| Function | Returns | Example |
|---|---|---|
strings.upper(s) | s in capitals | $(strings.upper(page.code.value)) |
strings.lower(s) | s in small letters | $(strings.lower(page.code.value)) |
strings.title(s) | Each word capitalised | $(strings.title(page.enter_name.value)) |
strings.truncate(s, n) | The first n characters plus ..., if s is longer than n | $(strings.truncate(rc.display_name, 20)) |
strings.is_blank(s) | true if s is unset or empty. Spaces count as text. | $(strings.is_blank(rc.display_name)) |
strings.has_prefix(s, p) | true if s starts with p | $(strings.has_prefix(page.phone.value, '23276')) |
strings.has_suffix(s, p) | true if s ends with p | $(strings.has_suffix(page.meter_number.value, '01')) |
strings.contains(s, p) | true if p is anywhere in s | $(strings.contains(page.enter_name.value, 'Kamara')) |
strings.split_index(s, d, i) | The piece at position i (from 0) after splitting s on d, or null | $(strings.split_index(rc.display_name, ' ', 0)) |
datetime
| Function | Returns | Example |
|---|---|---|
datetime.now() | The current time in UTC, as an ISO 8601 timestamp | $(datetime.now()) |
datetime.extract(t, part) | One part of timestamp t as a number. Pass null for now. | $(datetime.extract(null, 'HH')) |
part is one of:
yyyyyearMMmonth, 1 to 12dday of the monthEday of the week, 1 (Monday) to 7 (Sunday)HHhour, 0 to 23mmminutesssecond
Any other value stops the flow with an error.
subscriber
| Function | Returns | Example |
|---|---|---|
subscriber.is_one_of(list) | true if the subscriber's number is in list, a comma-separated list of numbers, each starting with +232 | $(subscriber.is_one_of('+23276000001,+23277000002')) |
Use it to run a page only for test phones:
{
"id": "mark_test_phone",
"component": { "type": "side_effect" },
"options": {
"skip": "$(!subscriber.is_one_of('+23276000001,+23277000002'))"
},
"on_done": {
"action": "next",
"session_context": { "mutations": { "is_test_phone": true } }
}
}Text, Numbers and Missing Values
- Every value read with
$(...)is text, even one you saved as a number ortrue. - Arithmetic turns number-like text into numbers:
page.enter_amount.value * 2works. +joins two pieces of text, sopage.fee.value + page.tax.valuegives5010for 50 and 10. Multiply one side by 1 to add them:page.fee.value * 1 + page.tax.value.- To test a saved
true, compare with text:rc.is_member == 'true'. - An
rcorsckey that was never saved isnull. Give it a default with??before you compare or calculate with it:(rc.lifetime_spend ?? 0) < 500. - Empty text counts as false. Use
strings.is_blank(x)to test for "unset or empty".
Missing page, data and session values behave differently; see Missing Values.
Example: Tiered Menu
A menu that offers more options as a subscriber spends more. Your server, or another page, keeps rc.lifetime_spend up to date.
{
"id": "select_option",
"component": {
"type": "menu",
"label": "Welcome $(rc.display_name ?? 'friend'). Pick an option:",
"items": [
{
"label": "Usual (SLE $(rc.usual_amount ?? 50))",
"value": "$(rc.usual_amount ?? 50)"
},
{
"label": "Premium (SLE 100)",
"value": "100",
"hide": "$((rc.lifetime_spend ?? 0) < 500)"
},
{
"label": "Gold (SLE 250)",
"value": "250",
"hide": "$((rc.lifetime_spend ?? 0) < 2000)"
}
]
},
"on_done": {
"action": "next",
"recurring_context": {
"mutations": { "usual_amount": "$(page.select_option.value)" }
}
}
}A new subscriber sees only "Usual (SLE 50)". Premium appears once lifetime spend reaches SLE 500, and Gold at SLE 2000. The amount chosen becomes the next visit's "Usual".
Limitations
Expressions can't:
- loop (
for,while) - create variables (
var x = ...) - define functions (
x -> ...) - create objects (
new) - change any value. Saving happens only through
on_donemutations. - index a list with a computed position (
list[i])
When you need any of these, do the work on your server through an exchange and read the result with $(data.KEY).