APL User-defined Commands
You can define custom commands in your Alexa Presentation Language (APL) document or package.
Overview of user-defined commands
The commands
property in the APL document defines a set of commands. A user-defined command can call other APL standard commands.
The following example creates a command called slideInFromRight
that calls the AnimateItem
command.
{
"commands": {
"slideInFromRight": {
"parameters": [
"distance",
{
"name": "duration",
"type": "number",
"default": 300
}
],
"command": {
"type": "AnimateItem",
"easing": "ease-in-out",
"duration": "${duration}",
"value": [
{
"property": "opacity",
"from": 0,
"to": 1
},
{
"property": "transform",
"from": {
"translateX": "${distance}"
},
"to": {
"translateX": 0
}
}
]
}
}
}
}
To use a parameterized command, pass the name of the command as the type.
{
"type": "TouchWrapper"
"onPress": {
"type": "slideInFromRight",
"duration": 1000,
"distance": "20vw"
}
}
In this example, the distance
parameter is bound to 20vw
in the translateX
property animation. The duration of the command is 1000 milliseconds because the value 1000 passed in the duration
parameter overrides the default 300 value defined in the command.
Because the command defines a default for duration
, you could call this command without providing the duration
parameter and get the 300 millisecond animation instead. In contrast, the distance
parameter doesn't have a default. If you call the command without distance
, the transform.translateX
property in the animation defaults to zero, so the "slide" animation is from zero to zero and the component doesn't move.
Command definitions
The command
property in the document is a map of command name to a command definition. The command definition contains the following properties:
Property | Type | Required | Description |
---|---|---|---|
parameters |
Array of parameter definitions | No | Optional named parameters to add to the data-binding context |
command , commands |
Array of commands | Yes | The array of commands to run |
description |
String | No | Description of this command |
commands
The command property is an implicit sequential block of commands to run. The array of commands supports the standard data-binding with array rules including automatic conversion of a single command into an array of commands and substitution of parameters into the array.
parameters
The parameters are named values that can be passed as arguments. Each parameter is an object with the following properties:
Property | Type | Required | Description |
---|---|---|---|
default |
Any | No | The default value to apply if this parameter isn't specified. Defaults to empty. |
description |
String | No | A user-provided description of this parameter |
name |
String | Yes | The parameter name |
type |
any , array , boolean , color , dimension , integer , map , number , object , string |
No | The type of the parameter. Defaults to any |
The parameter name must be a unique name starting with an upper- or lower-case letter and containing no white space ([a-zA-Z][a-zA-Z0-9]*
).
As a convenience, a simple parameter name can be used instead of the parameter object (for example, "title" instead of { "name": "title", ... }
. This isn't recommended, but does allow compact command definition for cases where no type coercion or defaults values are required.
Command inflation
Commands are inflated with the following algorithm:
- Each
parameter
is evaluated and added to the data-binding context. - The
command
array is evaluated. - Properties assigned to the object but not matching a named parameter are passed to each command as properties.
For example, note this definition of customPressHandler
:
"commands": {
"customPressHandler": {
"description": "Hide the current component and fire an event if checked."
"parameters": [
"onChecked",
{
"type": "number",
"name": "opacity",
"default": 0.5
}
],
"commands": [
{
"type": "SetValue",
"property": "opacity",
"value": "${opacity}"
},
{
"type": "Sequential",
"when": "${event.target.checked}",
"commands": [
"${onChecked}"
]
}
]
}
}
Now assume this custom command is invoked with:
{
"onPress": {
"type": "customPressHandler",
"onChecked": {
"type": "SendEvent",
"arguments": ["It was checked!"]
},
"delay": 500
}
}
When the onPress
handler fires, the inflated command representation is:
[
{
"type": "SetValue",
"property": "opacity",
"value": 0.5,
"delay": 500
},
{
"type": "Sequential",
"when": "${event.target.checked}",
"delay": 500,
"commands": [
{
"type": "SendEvent",
"arguments": ["It was checked!"]
}
]
}
]
Note the following:
- The original invocation passed a
delay
value of 500. ThecustomPressHandler
definition doesn't includedelay
as a parameter, so thedelay
value was passed into each of the commands in the custom command. - The original invocation didn't pass an
opacity
value, butopacity
is defined as a parameter incustomPressHandler
. So the data-binding context was extended by the default value ofopacity
, which is 0.5 in this example. That value was used in theSetValue
command expansion of${opacity}
.
Nested Expansion of User Commands
You can define user commands that reference other user commands. For example:
{
"SoftStagger": {
"description": "General soft-stagger sliding objects from the left or right",
"parameters": [
"delay",
"duration",
"distance"
],
"commands": [
{
"type": "SetValue",
"property": "opacity",
"value": 0
},
{
"type": "AnimateItem",
"values": [
{
"property": "opacity",
"to": 1
},
{
"property": "translateX",
"from": "${distance}",
"to": 0
}
],
"delay": "${delay}",
"duration": "${duration}",
"easing": "ease-out"
}
]
}
}
You can use the SoftStagger
command as is and pass in the parameters for
delay
, duration
, and distance
. You can also use SoftStagger
to define some standard variations for convenience:
{
"SoftStagger1": {
"commands": {
"type": "SoftStagger",
"delay": 0,
"duration": 666,
"distance": 40
}
},
"SoftStagger2": {
"commands": {
"type": "SoftStagger",
"delay": 50,
"duration": 666,
"distance": 40
}
}
}
You can then add a soft stagger entrance animation to a component with a line like this: onMount:{"type": "SoftStagger2"}
.
Last updated: Jan 03, 2025