APL Data-Binding Syntax (APL 1.7 to 2023.1)
(This is not the most recent version of APL. Use the Other Versions option to see the documentation for the most recent version of APL)
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 |
|
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
|
|
Array.slice(array,start[,end]) |
Return the subset of
|
${Array.slice(a,3)} == [104,105,106] ${Array.slice(a,1,3)} == [102,103] ${Array.slice(a,-2)} == [105,106] |
Math functions
The top-level Math
property is a collection of functions and constants for numerical calculations.
Function | Description | Example |
---|---|---|
|
The absolute value of x |
|
|
The arccosine of x |
|
|
The hyperbolic arccosine of x |
|
|
The arcsine of x |
|
|
The hyperbolic arcsine of x |
|
|
The arctangent of x |
|
|
The hyperbolic arctangent of x |
|
|
The arc tangent of y/x |
|
|
The cube root of x |
|
|
The smallest integer greater than or equal to x. |
|
|
Return x if y<x, z if y>z and otherwise y. |
|
|
The cosine of x |
|
|
The hyperbolic cosine of x |
|
|
e raised to the x, where e is Euler's constant. |
|
|
2 raised to the x |
|
|
e raised to the x minus 1. |
|
|
Converts x into a floating-point number. A trailing '%' character specifies a percentage. |
|
|
The largest integer less than or equal to x. |
|
|
The square root of the sum of the squares of the arguments |
|
|
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. |
|
|
Returns true when x is finite. Returns false when x is infinite or is Not-A-Number (NaN) |
|
|
Returns true when x is infinite |
|
|
Returns |
|
|
The natural logarithm of x |
|
|
The natural logarithm of 1+x |
|
|
The base-10 logarithm of x |
|
|
The base-2 logarithm of x |
|
|
The largest argument |
|
|
The smallest argument |
|
|
Raises x to the y power |
|
|
A random number between 0 and 1 |
|
|
Return the nearest integer to x |
|
|
The sign of x: |
|
|
The sine of x |
|
|
The hyperbolic sine of x |
|
|
The square root of x |
|
|
The tangent of x |
|
|
The hyperbolic tangent of x |
|
|
The integer portion of x |
|
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.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.
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, the developer 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 |
For example, to show elapsedTime
after the document has loaded:
{
"type": "Text",
"text": "${Time.format('mmm:ss.S', elapsedTime)}"
}
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'
Last updated: Feb 29, 2024