APL Data-Binding Syntax


You use data-binding syntax to write expressions that Alexa evaluates when displaying your Alexa Presentation Language (APL) document. You use data-binding expressions to bind component properties to your data source, and to write conditional logic to hide and show components depending on criteria such as the viewport characteristics.

About data binding expressions

You use data binding expressions inside JSON strings. A data binding expression has the form "${expression}". You can use any number of expressions inside a string, for example: "${2}+${2} = ${2+2}".

Expressions are evaluated within the current data-binding context. The data-binding context is a global dictionary that supports boolean values, numbers, strings, arrays, objects, null, and references to defined resources.

Supported value types

Identifier

An identifier is a name used to identify a data-binding variable. Identifiers must follow the C identifier naming convention: [a-zA-Z_][a-zA-Z0-9_]*. This means that the identifier must start with an upper or lower-case ASCII letter or underscore, followed by zero or more ASCII letters, numbers, or the underscore:

${data}
${_myWord23}
${__AnUgly26_letter__examplE}

String literals

Strings are defined using either single or double quotes. The starting and ending quote must match. Escape quotes, carriage returns, and line-feed characters.

	${"Double-quoted string"}
	${'Single-quoted string'}
	${"Inner quote: \" or '"}

Expressions can be nested inside of a string.

	${"Two plus two is ${2+2}"}

Numbers

Positive, negative, and floating point numbers are supported. Scientific notation isn't supported. All numbers are internally stored as doubles:

${1}
${-34.75}
${64000000000}

Boolean values

Boolean values of true and false are supported.

	${true}
	${false}

null

The null constant is supported.

	${null}

Array

Define arrays inside of a data-binding expression by providing a comma-separated list of expressions within square brackets:

${["A", "B", "C"].length}     // Evaluates to 3
${["on","off"][0]}            // Evaluates to "on"
${[true,"true",1][0]}         // Evaluates to boolean true

Maps

Define a map object inside of a data-binding expression by providing a string key and value pairs within curly brackets.

${{"A": 1, "B": 2}["A"]}    // Evaluates to 1

Resources

Resources defined in the data-binding context use the reserved character @. For example:

${@myBlue}
${@isLandscape ? @myWideValue : @myNarrowValue}

APL packages define resources exposed in data-binding.

Absolute dimensions

A dimensional suffix converts a number into an absolute viewport dimension. The valid dimensional suffixes are dp, px, vh, and vw:

${23 dp}     // 23 display-independent pixels
${10 px}     // 10 pixels
${50 vw}     // 50% of the width of the viewport
${100vh}     // 100% of the height of the viewport

Dimensional suffixes must be attached to a number, not a more complex expression. For example ${(20*20) dp} isn't valid, but ${20*20dp} is valid.

Truthy and coercion

Data-binding expressions involve different data types. These types can convert into other types. The following table gives an example of the different conversions. The examples shown in the table assume a viewport width of 512dp and a dpi of 320.

Object Example As Boolean As Number As String As Color As Dimension
Null null false 0 "" transparent 0dp
Boolean true true 1 "true" transparent 0dp
Boolean false false 0 "false" transparent 0dp
Number 23 true 23 "23" #00000017 23dp
Number 0 false 0 "0" transparent 0dp
String "My dog" true 0 "My dog" transparent 0dp
String "" false 0 "" transparent 0dp
String "-2.3" true -2.3 "-2.3" transparent -2.3dp
String "red" true 0 "red" #ff0000ff 0dp
String "50vw" true 50 "50vw" transparent 256dp
Array [] true 0 "" transparent 0dp
Map {} true 0 "" transparent 0dp
Color red true 0 "#ff0000ff" #ff0000ff 0dp
Dimension 32px true 16 "16dp" transparent 16dp
Dimension 0vh false 0 "0dp" transparent 0dp
Dimension 23% true 0.23 "23%" transparent 23%
Dimension 0% false 0 "0%" transparent 0%
Dimension auto true 0 "auto" transparent auto
Anything else ... true 0 "" transparent 0dp
             

Boolean coercion

A truthy value is a value that's considered true when evaluated in a boolean context. All values are truthy except for false, 0, "", a zero absolute or relative dimension, and null.

Number coercion

The Boolean true value converts to the number 1. String values are converted using the C++ std::stod method, which is influenced by the locale. Absolute dimensions convert to the number of display-independent pixels in the absolute dimension. Relative dimensions convert to the percentage value (for example, 32% converts to 0.32). Everything else converts to 0.

String coercion

Internal types convert to strings according to the rules shown in the following table:

Object Example Result Description
Null null '' The null value isn't displayed.
Boolean true 'true' Boolean true and false display as strings.
  false 'false'  
Number -23 '-23' Integers have no decimal places.
  1/3 '0.333333' Non-integers have decimal places.
String "My \"dog\" " 'My "dog" ' String values.
Array [...] '' Arrays don't display.
Map {...} '' Maps don't display.
Color red '#ff0000ff' Colors display in #rrggbbaa format.
Dimension 23 dp '20dp' Absolute dimensions display with the suffix dp
Dimension 20 % '20%' Percentage dimensions display with the suffix %
Dimension auto 'auto' The auto dimension displays as 'auto'
Anything else ${Math.min} '' Math functions don't display.

The specific format of non-integer numbers isn't defined, but should follow the C++ standard for sprintf(buf, "%f", value). The format might change based on the locale.

Color coercion

Color values are stored internally as 32-bit Red-Green-Blue-Alpha (RGBA) values. Numeric values are treated as unsigned 32-bit integers and converted directly. String values are parsed according to the rules in Data Types - Color.

Absolute dimension coercion

Numeric values are assumed to be measurements in dp and are converted to absolute dimensions. String values are parsed according to the rules in Data Types - Dimension. All other values are 0.

Relative dimension coercion

Numeric values are assumed to be percentages and are converted directly. For example, 0.5 converts to 50%. Strings are parsed according to the rules in Data Types - Dimension. All other values are 0.

Operators

APL supports the following types of operators: arithmetic, logical, comparison, null coalescing, and ternary.

Arithmetic operators

APL supports the standard arithmetic operations for addition, subtraction, multiplication, division, and remainder.

${1+2}  // 3
${1-2}  // -1
${1*2}  // 2
${1/2}  // 0.5
${1%2}  // 1.

Addition and subtraction work for pairs of numbers, absolute dimensions, and relative dimensions. When a number is combined with either an absolute or relative dimension, the number is coerced into the appropriate dimension.

The addition operator also acts as a string-concatenation operator if either the left or right operand is a string.

${27+''}     // '27'
${1+' dog'}  // '1 dog'
${'have '+3} // 'have 3'

Multiplication, division, and the remainder operator work for pairs of numbers. Multiplication also works if the one of the operands is a relative or absolute dimension and the other is a number. This calculation results in a dimension. Division works if the first operand is a relative or absolute dimension and the second is a number. This calculation results in a dimension.

The remainder operator behaves as in JavaScript.

${10 % 3}  // 1
${-1 % 2}  // -1
${3 % -6}  // 3
${6.5 % 2} // 0.5

Logical operators

APL supports the standard logical and/or/not operators.

${true || false}   // true
${true && false}   // false
${!true}           // false

The && returns the first operand if it isn't truthy and the second otherwise. The || operator returns the first operand if it's truthy and the second otherwise.

${7 && 2}    // 2
${null && 3} // null
${7 || 2}    // 7
${0 || -16}  // -16

Comparison operators

Comparison operators return boolean values.

${1 < 2}
${75 <= 100}
${3 > -1}
${4 >= 4}
${myNullValue == null}
${(2>1) == true}
${1 != 2}

The comparison operators don't apply to arrays and objects.

Comparison operators don't perform type coercion. For example, the expression ${1=='1'} returns false. The following table lists the valid comparisons. All other comparisons return false.

Comparison types <, >, <=, >= ==, != Notes

Number to Number

Valid

Valid

Number to Absolute Dimension

Valid

Valid

The number is treated as a display-independent pixel dimension

Number to Relative Dimension

Valid

Valid

The number is treated as a percentage. For example, 0.4 equals 40%.

Relative Dimension to Relative Dimension

Valid

Valid

The dimensions are compared as percentages.

Absolute Dimension to Absolute Dimension

Valid

Valid

The dimensions are converted to display-independent pixels.

String to String

Valid

Valid

Boolean to Boolean

False

Valid

Color to Color

False

Valid

Null to Null

False

Valid

${null==null} is true

Auto to Auto Dimension

False

Valid

Two auto dimensions are equal (even when the final size isn't equal)

All other combinations

False

False

The == operator in APL is similar to the === operator in JavaScript.

Null coalescing

The ?? operator is the null-coalescing operator. It returns the left-hand operand if the operand isn't null; otherwise it returns the right-hand operand. You can chain the null-coalescing operator.

${person.name ?? person.surname ?? 'Hey, you!'}

The null-coalescing operator returns the left-hand operand if it's anything but null:

${1==2 ?? 'Dog'}   // returns false
${1==2 || 'Dog'}   // returns 'Dog'

Ternary operator

The ternary conditional operator ${a ? b : c} evaluates the left-hand operand. If it evaluates to true or a truthy value, the middle operand is returned. Otherwise the right-hand operand is returned.

${person.rank > 8 ? 'General' : 'Private'}

Array and object access

Array

Array access uses the [] operator, where the operand must be an integer. Arrays also support the .length operator to return the length of the array. Accessing an element outside of the array bounds returns null.

${myArray[4]}     // 5th element in the array (0-indexed)
${myArray.length} // Length of the array
${myArray[-1])}   // Last element in the array
${myArray[myArray.length]}  // Returns null (out of bounds)

Passing a negative index counts backwards through the array.

${a[-1] == a[a.length - 1]}  // True

Object

Objects support the . operator and the [] array access operator with string values.

${myObject.name}    // The 'name' property of myObject
${myObject['name']} // The 'name' property of myObject

If the property isn't defined, the expression returns null.

Calling the . or [] operator on null returns null.

${myNullObject.address.zipcode}  // Returns null

The right-side operand of the dot operator must be a valid identifier.

Function calls

Data-binding supports a limited number of built-in functions. Functions use the following form.

functionName( arg1, arg2, … )

Functions don't require arguments. A function returns a single value. The following examples show a variety of function expressions.

${Math.floor(1.1)}       // 1
${Math.ceil(1.2)}        // 2
${Math.round(1.2)}       // 1
${Math.min(1,2,3,4)}     // 1
${Math.max(1,2,3,4)}     // 4
${String.toUpperCase('Hello')}    // HELLO
${String.toLowerCase('Hello')}    // hello
${String.slice('Hello', 1, -1)}   // ell

The available functions are grouped by top-level property:

Array functions

The top-level Array property is a collection of functions and constants for manipulating arrays.

The examples in the Example column use the following array: a = [101,102,103,104,105,106].

Function Description Example
Array.indexOf(x,y) The index of element y in array x. Returns -1 if the array x doesn't contain y. ${Array.indexOf(a,102)} == 1
Array.range(start, end, [step])

Return an array of integer elements that starts from start, increments by step, and ends before end.

  • step is optional and defaults to 1
  • start is optional and defaults to zero. You must also leave out step and provide just end. For example, Array.range(10) returns an array of integers from 0 to 9.
  • Invalid arguments, such as Array.range(0), Array.range(10,3), or Array.range(1,5,0), return an empty array.

${Array.range(1,10,2)} == [1,3,5,7,9]

${Array.range(2,5)} == [2,3,4]

${Array.range(10)} == [0,1,2,3,4,5,6,7,8,9

Array.slice(array,start[,end])

Return the subset of array, starting at index start and extending to but not including index end.

  • end is optional and defaults to the length of the array.
  • A negative start or end value selects from the end of the array.
${Array.slice(a,3)} == [104,105,106]
${Array.slice(a,1,3)} == [102,103]
${Array.slice(a,-2)} == [105,106]

Log functions

The top-level Log property provides convenience methods for dealing with logging messages emitted by the Log command.

Function Description Example

Log.levelName(x)

Name of the log level

${Log.levelName(Log.DEBUG)} == 'debug'

Log.levelValue(x)

Value of the log level

${Log.levelValue('info') > Log.levelValue('debug')} == true

The following constants are available as numeric equivalents of the levels described in the Log command, listed in increasing numerical order:

  • Log.DEBUG
  • Log.INFO
  • Log.WARN
  • Log.ERROR
  • Log.CRITICAL

The specific values of these constants are subject to change. Therefore, don't use the values directly. You can rely on the relationship between these constants: Log.DEBUG < Log.INFO < Log.WARN < Log.ERROR < Log.CRITICAL.

Map functions

The top-level Map property is a collection of functions for working with Maps.

In the examples shown in the following table, x is the map {'a': 'alpha', 'b': 'bravo'}.

Function Description Example

Map.keys(x)

An array of the keys in the map x

${Map.keys(x)} == ['a', 'b']

Calling Map.keys(x) on anything but a map returns an empty array. The order of the keys returned from calling Map.keys(x) on a non-empty map is undefined.

The following example includes a data source called mapData, which is a map of letters to words. The document displays a Sequence and uses the Map.keys function to assign the data property to an array of all the keys in the map. Then the Sequence can access the key with the expression ${data} and the value with the expression ${mapData[data]}.


Math functions

The top-level Math property is a collection of functions and constants for numerical calculations.

Function Description Example

Math.abs(x)

The absolute value of x

${Math.abs(-2.3)} == 2.3

Math.acos(x)

The arccosine of x

${Math.acos(1)} == 0

Math.acosh(x)

The hyperbolic arccosine of x

${Math.acosh(1)} == 0

Math.asin(x)

The arcsine of x

${Math.asin(0)} == 0

Math.asinh(x)

The hyperbolic arcsine of x

${Math.asinh(2)} == 1.4436354751788103

Math.atan(x)

The arctangent of x

${Math.atan(1)} == 0.7853981633974483

Math.atanh(x)

The hyperbolic arctangent of x

${Math.atanh(0.5)} == 0.5493061443340548

Math.atan2(y,x)

The arc tangent of y/x

${Math.atan2(1,0)} == 1.5707963267948966

Math.cbrt(x)

The cube root of x

${Math.cbrt(8)} == 2

Math.ceil(x)

The smallest integer greater than or equal to x.

${Math.ceil(2.3)} == 3

Math.clamp(x,y,z)

Return x if y<x, z if y>z and otherwise y.

${Math.clamp(1, 22.3,10)} == 10

Math.cos(x)

The cosine of x

${Math.cos(0)} == 1

Math.cosh(x)

The hyperbolic cosine of x

${Math.cosh(0)} == 1

Math.exp(x)

e raised to the x, where e is Euler's constant.

${Math.exp(1)} == 2.718281828459045

Math.exp2(x)

2 raised to the x

${Math.exp2(5)} == 32

Math.expm1(x)

e raised to the x minus 1.

${Math.expm1(1)} == 1.718281828459045

Math.float(x)

Converts x into a floating-point number. A trailing '%' character specifies a percentage.

${Math.float('23.4')} == 23.4
${Math.float('23.4%')} == 0.234

Math.floor(x)

The largest integer less than or equal to x.

${Math.floor(2.3)} == 2

Math.hypot(x1,x2,…)

The square root of the sum of the squares of the arguments

${Math.hypot(3,4)} == 5

Math.int(x,[b=10])

Convert x into an integer. b is the optional base to use for string conversion, where 0 auto-detects or 2–36 define the base.

${Math.int('23.3')} == 23
${Math.int('20', 16)} == 32
${Math.int('0x20', 0)} == 32

Math.isFinite(x)

Returns true when x is finite. Returns false when x is infinite or is Not-A-Number (NaN)

${Math.isFinite(1.0)} == true
${Math.isFinite(1/0)} == false
${Math.isFinite(0/0)} == false

Math.isInf(x)

Returns true when x is infinite

${Math.isInf(1/0)} == true
${Math.isInf(-1/0)} == true
${Math.isInf(0/0)} == false
${Math.isInf(1.0)} == false

Math.isNaN(x)

Returns true when x is Not a Number (NaN)

${Math.isNaN(0/0)} == true
${Math.isNaN(1/0)} == false
${Math.isNaN(1.0)} == false

Math.log(x)

The natural logarithm of x

${Math.log(Math.E)} == 1

Math.log1p(x)

The natural logarithm of 1+x

${Math.log1p(1)} == 0.6931471805599453

Math.log10(x)

The base-10 logarithm of x

${Math.log10(100)} == 2

Math.log2(x)

The base-2 logarithm of x

${Math.log2(32)} == 5

Math.max(x1,x2,…)

The largest argument

${Math.max(2,3)} == 3

Math.min(x1,x2,…)

The smallest argument

${Math.min(2,3)} == 2

Math.pow(x,y)

Raises x to the y power

${Math.pow(3,4)} == 81

Math.random()

A random number between 0 and 1

${Math.random()} == 0.7113654073137101 (the actual random number returned is different)

Math.round(x)

Return the nearest integer to x

${Math.round(2.3)} == 2

Math.sign(x)

The sign of x: -1, 0, or +1

${Math.sign(-43.1) == -1

Math.sin(x)

The sine of x

${Math.sin(Math.PI/6)} == 0.5

Math.sinh(x)

The hyperbolic sine of x

${Math.sinh(1)} == 1.1752011936438014

Math.sqrt(x)

The square root of x

${Math.sqrt(9)} == 3

Math.tan(x)

The tangent of x

${Math.tan(Math.PI/4)} == 1

Math.tanh(x)

The hyperbolic tangent of x

${Math.tanh(10)} == 0.9999999958776927

Math.trunc(x)

The integer portion of x

${Math.trunc(-1.2)} == -1

Constant Description Value
Math.E Euler's constant (e) 2.718281828459045
Math.LN2 Natural logarithm of 2 0.6931471805599453
Math.LN10 Natural logarithm of 10 2.302585092994046
Math.LOG2E Base-2 logarithm of e 1.4426950408889634
Math.LOG10E Base-10 logarithm of e 0.4342944819032518
Math.PI Ratio of the circumference of a circle to the diameter (π) 3.141592653589793
Math.SQRT1_2 Square root of 0.5 0.7071067811865476
Math.SQRT2 Square root of 2 1.4142135623730951

String functions

The top-level String property is a collection of functions for manipulating strings.

Function Description Example
String.charAt(x,y) Return the character at index y. If y is a negative number, select from the end of the string. ${String.charAt('école', 0)} == 'é'

${String.charAt('école', -2)} == 'l'
String.length(x) Return the length of the string ${String.length('schön')} == 5
String.slice(x,y[,z]) Return the subset of x starting at index y and extending to but not including index z. If z is omitted, the remainder of the string is returned. If y is a negative number, select from the end of the string. ${String.slice('berry', 2, 4)} == 'rr'

${String.slice('berry', -2)} == 'ry'
String.toLowerCase(x) Lowercase the string ${String.toLowerCase('bEn')} == 'ben'
String.toUpperCase(x) Uppercase the string ${String.toUpperCase('bEn')} == 'BEN'

The string length and slice functions work on Unicode code points, not bytes.

The following example shows all the available String functions. This example also uses deferred evaluation. The data source stores the function examples as data-binding expressions with the #{...} placeholder. This lets the document display the text of the function, and then also use eval() to evaluate the function to display the result.


Time functions

The top-level Time property is a collection of functions that convert from time values in milliseconds into years, months, days, hours, minutes, and seconds. The examples in the table assume a bound time value T=1567786974710, which in human-readable terms is Friday September 6, 2019 at 16:22:54 and 710 milliseconds.

Function Description Example
Time.year(x) The year ${Time.year(T)} == 2019
Time.month(x) The month (from 0 through 11) ${Time.month(T)} == 8 (September)
Time.date(x) The date of the month (1-31) ${Time.date(T)} == 6
Time.weekDay(x) The day of the week (0-6) ${Time.weekDay(T)} == 5 (Friday)
Time.hours(x) The hour of the day (0-23) ${Time.hours(T)} == 16
Time.minutes(x) The minutes of the hour (0-59) ${Time.minutes(T)} == 22
Time.seconds(x) The seconds in the minute (0-59) ${Time.seconds(T)} == 54
Time.milliseconds(x) The milliseconds (0-999) ${Time.milliseconds(T)} == 710
Time.format(f, x) A formatted text string ${Time.format('H:mm', T)} == "16:22"

To construct a basic digital clock, you could use the Time functions to build a 24-hour clock out of a Text component and the localTime property:

{
  "type": "Text",
  "bind": {
    "name": "T",
    "value": "${localTime}"
  },
  "text": "${Time.hours(T)}:${Time.minutes(T)}"
}

This example binds the value of localTime into a local variable T to make the text expression shorter. This clock renders a time like 4:04 PM as 16:4, which might not be what you want. You can use conditional statements to determine the time and include leading/trailing zeros if necessary.

You can also use conditional statement to display a 12-hour clock as shown in this example. This binds both the hours and minutes to local variables, and then uses conditional statements to output the time in a 12-hour format with am or pm.

{
  "type": "Text",
  "bind": [
    {
      "name": "h",
      "value": "${Time.hours(localTime)}"
    },
    {
      "name": "m",
      "value": "${Time.minutes(localTime)}"
    }
  ],
  "text": "${h >= 12 ? h - 12 : h}:${m < 10 ? '0' : ''}${m} ${h >= 12 ? 'pm' : 'am'}"
}

You can also use the Time.format function to simplify the code. This example also uses a conditional statement based on hours to add the "am" or "pm" at the end.

{
  "type": "Text",
  "bind": [
    {
      "name": "h",
      "value": "${Time.hours(localTime)}"
    }
  ],
  "text": "${Time.format('h:mm', localTime) + (h >= 12 ? ' pm' : ' am')}"
}

Time.format

The format function takes a string argument containing the formatting codes and a time value. The following formatting codes are supported:

Value Range Description
YY 00..99 Year, two digits
YYYY 1970..XXXX Year, four digits
M 1..12 Month (1=January)
MM 01..12 Month, two digits (1=January)
D 1..31 Day of the month
DD 01..31 Day of the month, two digits
DDD 0..N Days, any number of digits
H 0..23 24h hour
HH 00..23 24h hour, two digits
HHH 0..N Hours, any number of digits
h 1..12 12h hour
hh 01..12 12h hour, two digits
m 0..59 Minutes
mm 00..59 Minutes, two digits
mmm 0..N Minutes, any number of digits
s 0..59 Seconds
ss 00..59 Seconds, two digits
sss 0..N Seconds, any number of digits
S 0..9 Deciseconds
SS 00..99 Centiseconds
SSS 000..999 Milliseconds

All the formatting codes return digits only.

The following table shows examples of the formatting functions, using the time value September 6, 2019 at 16:22:54 and 710 milliseconds.

Format Value
DD-MM-YYYY 06-09-2019
M/D/YY 9/6/19
DDD days 18145 days (after the epoch)
H:mm 16:22
hh:mm 04:22
H:mm:ss 16:22:54

Time formatting also works for relative times from timers. The following table shows examples that use the value 7523194, which corresponds to two hours, five minutes, and 23.194 seconds.

Format Value
mmm:ss.S 125:23.1
HHH:mm:ss.SS 2:05:23.19
sss.SSS 7523.194

The following example uses elapsedTime to show the time after the document has loaded. To restart the timer in the sandbox, refresh the page.


Data-binding string conversion

Because APL is serialized in JSON, all data-bound expressions are defined inside of a JSON string:

{
  "MY_EXPRESSION": "${....}"
}

When there are no spaces between the quotation marks and the data-binding expression, the result of the expression is the result of the data-binding evaluation. For example:

"${true}"               -> Boolean true
"${2+4}"                -> Number 6
"${0 <= 1 && 'three'}"  -> String 'three'

When extra spaces are in the string outside of the data-binding expression or when two data-binding expressions are juxtaposed, the result is a string concatenation:

" ${true}"     -> String ' true'
"${2+4} "      -> String '6 '
"${2+1}${1+2}" -> String '33'

Deferred evaluation

When APL evaluates a property or bound variable, expressions written with the ${...} syntax are evaluated and resolved. Sometimes, you might want to defer this evaluation to later and control when to resolve an expression. With APL version 2023.2 and later, you can use the #{...} placeholder and the eval() built-in function to defer expression evaluation. You can also use eval() to evaluate expressions that aren't evaluated automatically, such as expressions within a data source.

Deferred placeholder syntax

Data-binding evaluation replaces the #{...} placeholder with a standard data-binding expression, but doesn't evaluate that expression. This means that the ${...} syntax remains. The following example shows a Text component that uses the #{...} syntax in the text property.

{
  "type": "Text",
  "text": "The equation #{1+2} evaluates to ${1+2}"
}

Normal evaluation on the string results in the string "The equation ${1+2} evaluates to 3". The ${1+2} expression remains in the string.

The expression inside of the #{...} placeholder must be a valid APL expression or the replacement fails. The following example shows a bind array with two items. The variable X resolves to ${1+a}. The variable Y fails to evaluate because 1+ isn't a valid expression. Therefore, the Y contains #{1+}.

"bind": [
  {
    "name": "X",
    "value": "#{1+a}"
  },
  {
    "name": "Y",
    "value": "#{1+}"
  }
]

eval() function

The eval() built-in function takes a single argument and runs the data-binding Data-binding algorithm on the argument. The argument is either a string that was created with the #{...} placeholder or a string provided in a data source.

The following example shows three bind items. Format uses the deferred data binding syntax, and Greeting calls the eval() function.

"bind": [
  {
    "name": "Format",
    "value": "Hello, #{Name}"
  },
  {
    "name": "Name",
    "value": "Chris"
  },
  {
    "name": "Greeting",
    "value": "${eval(Format)}"
  }
]

The bindings resolve to the following values:

Format   = "Hello, ${Name}"
Name     = "Chris"
Greeting = "Hello, Chris"

You can nest the eval() calls, as shown in the following example.

"bind": [
  {
    "name": "A",
    "value": "#{B}"
  },
  {
    "name": "B",
    "value": "#{C}"
  },
  {
    "name": "C",
    "value": "Hello"
  },
  {
    "name": "D",
    "value": "${eval(A)}"
  },
  {
    "name": "E",
    "value": "${eval(eval(A))}"
  }
]

The bindings in this example resolve to the following values:

A = "${B}"
B = "${C}"
C = "Hello"
D = "${C}"
E = "Hello"

Note that calling eval(A) results in "${C}", not "Hello".

Use deferred evaluation for resource localization

With deferred evaluation, you can embed dynamic data within items defined as resources. This technique is useful for formatting language-specific strings.

The following example defines a TEMPERATURE_FORMAT resource that uses different formatting for different locales. Change the locale in the data source to see the screen update.


The example adapts to the language and converts the temperature to a region-appropriate format. For the en-US environment, APL sets the resources to the following values:

CELSIUS             = "${TEMP} °C"
FAHRENHEIT          = "${TEMP * 9 / 5 + 32} °F"
TEMPERATURE_FORMAT = "The temperature is ${TEMP * 9 / + 32} °F" // "en-US" clause selected
text                = "The temperature is 77 °F"

The eval() call in the text property evaluates the TEMPERATURE_FORMAT string using TEMP=25.0, which results in "The temperature is 77 °F". If you change the value of TEMP, such as with the SetValue, the text property re-evaluates the expression and updates the component.

Deferred evaluation and data sources

You can use the eval() function to evaluate data-binding expressions passed to your document in a data source.

By default, data binding evaluation doesn't process expressions within a data source. For example, assume you set a data source property to the expression ${1+2} and then bind that data source property to a component in your document. When the component inflates, the component property contains the text "${1+2}" and not the evaluated result of "3".

To process data binding expressions within data source properties, pass the property to the eval() function.

The following example shows how you can use the eval() function with a data source.


How eval() processes arrays and objects

An eval() function call on an array or object evaluates each array entry and object property separately. This feature can be useful to process a set of formatting strings.

For example, assume X is an object with two properties that contain data-binding expressions.

X = {
  "formal_greeting": "Hello, ${NAME}",
  "casual_greeting": "Hi ${NAME}!"
}
NAME = "Raj"

If you pass X to eval(), the function returns an object in which each property is now resolved.

eval(X) = {
  "formal_greeting": "Hello, Raj",
  "casual_greeting": "Hi Raj!"
}

The following example shows an APL document that invokes eval() on an object.


Deferred evaluation guidelines

The eval() function is expensive

The eval() function parses and evaluates the expression each time you call the function. This means that the eval() function is an expensive operation. Before you use eval(), consider whether you could use a different approach.

Avoid recursive calls to eval()

Be careful not to create recursive calls to eval() that never end.

The following example creates a bind variable A that calls eval() on itself.

Example of a recursive call

{
  "type": "Text",
  "bind": {
    "name": "A",
    "value": "HA! #{eval(A)}"
  },
  "text": "I'm invincible! ${eval(A)}!"
}

APL limits the depth of recursive evaluation calls. Therefore, this example might display text such as "I'm invincible! HA! HA! HA! ${eval(A)}" instead of an infinitely long string. The limit is guaranteed to be at least 3, but might be higher in a specific runtime.


Was this page helpful?

Last updated: Mar 11, 2024