Skip to main content
Version: 2.4

JEXL Practices And Cheat Sheet

Keep expressions short. If an expression combines an API call, multiple nested ternary branches, and several transformations, it will be hard to maintain.

Use ?? for numeric fallbacks:

$context.form.amount ?? 0

Use || for text where an empty string means "no value":

$context.form.name || 'No name'

For custom validation, always verify whether the field expects true as "valid" or as "invalid". In schema customValidations, current form examples use true as "valid".

Use Public API calls only where they are necessary. An expression can be evaluated multiple times while the user edits the form.

For date/time expressions, distinguish:

  • addTime returns ISO with offset,
  • addTimeIso returns UTC ISO with Z,
  • now(true) sets time to start of day,
  • midnight() returns end of day.

For less common helpers, verify availability in the JEXL debug console before using them in production configuration.

Use deprecated helpers only when maintaining existing configuration. For new date expressions, prefer dateFormat, toIso, toISOString, or addTimeIso over dateServerFormat.

Common Mistakes

Reversed Hidden Logic

In a Hidden expression, true means hide:

$context.form.segment == 'B2B'

This expression does not mean "visible for B2B"; it hides the field for B2B.

Losing Zero With ||

$context.form.count || 1

If count is 0, the result is 1. For numbers, use:

$context.form.count ?? 1

Unguarded Nested Access

$context.form.customer.name

If customer is not filled, the expression can fail. Safer form:

$context.form.customer ? $context.form.customer.name : null

API Call In A Frequently Evaluated Field

crm.customer.find({ code: $context.form.customerCode })

This is fine for a targeted lookup, but it should not be part of an expression that is recalculated on every keystroke without debounce or cache.

Assuming Full JavaScript Runtime

JEXL is not full JavaScript. Do not assume helpers such as keys(...) or Object.keys(...) are available in a configured field:

keys($context.form)

Use a registered helper, an explicit array of known keys, or a backend script:

[
{ key: 'productInternet', value: $context.form.productInternet },
{ key: 'productTv', value: $context.form.productTv },
{ key: 'productVoice', value: $context.form.productVoice }
].filter(x => x.value != null)

For less common helpers, verify the expression in the JEXL debug console before using it in production configuration.

Quick Cheat Sheet

NeedExpression
Todaynow(true)
Current timenow()
Add 30 daysaddTimeIso($context.form.validFrom, 30, 'days')
Format datedateFormat($context.form.date, 'dd.MM.yyyy')
Sum pricessumMany($context.form.items, 'price', 2)
Empty valueisNullOrEmpty($value)
Email validation$value && !isValidEmail($value)
Array contains valueincludes($context.form.roles, 'ADMIN')
Array contains at least one valueincludesArray($context.form.roles, ['ADMIN', 'SUPPORT'], true)
First primary contactfind($context.form.contacts, 'primary', true)
Contact namesjoin(map($context.form.contacts, 'name'), ', ')
Create objectcreateObject('customer.id', $context.form.customerId)
Public API getcrm.customer.get($context.form.customerId)
Public API filtercrm.customer.find({ code__in: $context.form.customerCodes })
Translate label`'order.installation.self'
Show object as JSON`$context.form.productInternet
Parse configured JSON`$context.form.rawJson ? ($context.form.rawJson
Legacy date conversionExisting dateServerFormat(...) only; do not use for new fields