-
Notifications
You must be signed in to change notification settings - Fork 7
Advanced Topics
JSX for APL provides contracts for APL Commands. Therefore it leverages command declarations within typescript usage. The set of usable commands interfaces can be found under Which APL Components does JSX for APL provide section.
The following sample demonstrates the usage of AnimateItem
command inside onMount
property.
// FruitApl.tsx
import * as React from "react";
import { Fruit } from "./data_types";
import { Frame, Container, Text, Image, APL, MainTemplate, Command } from "ask-sdk-jsx-for-apl";
export class FruitApl extends React.Component<{ fruit: Fruit }> {
render() {
return (
<APL>
<MainTemplate>
<Frame width="100vw" height="100vh" backgroundColor="rgb(22,147,165)">
<Container
width="100vw"
height="100vh"
alignItems="center"
justifyContent="spaceAround"
>
<Text text={this.props.fruit.title} fontSize="50px" color="rgb(251,184,41)" />
<Image
source={this.props.fruit.image}
height="60vh"
width="30vh"
scale="best-fit"
onMount={[this.buildAnimateCommand()]}
/>
</Container>
</Frame>
</MainTemplate>
</APL>
)
}
buildAnimateCommand(): Command {
const command: Command = {
type: "AnimateItem",
duration: 600,
easing: "ease-in-out",
value: [
{
property: "opacity",
from: 0,
to: 1,
},
{
property: "transform",
from: [
{
translateX: 200,
},
{
rotate: 90,
},
],
to: [
{
translateX: "0",
},
{
rotate: 0,
},
],
},
],
};
return command;
}
}
In case of using Execute Command Directive with JSX for APL, token
property must be provided to APL component: <APL token="token_name">
.
// JumpIntentHandler.tsx
import * as React from "react";
import { HandlerInput, RequestHandler } from "ask-sdk";
import { Command } from "ask-sdk-jsx-for-apl";
import { interfaces } from "ask-sdk-model";
export class JumpIntentHandler implements RequestHandler {
canHandle(handlerInput: HandlerInput): boolean | Promise<boolean> {
//...
}
handle(handlerInput: HandlerInput) {
const responseBuilder = handlerInput.responseBuilder;
responseBuilder.addDirective({
type: "Alexa.Presentation.APL.ExecuteCommands",
token: "animalAplToken",
commands: [
this.getJumpCommand() as interfaces.alexa.presentation.apl.SequentialCommand,
],
});
return responseBuilder.getResponse();
}
getJumpCommand(): Command {
const jumpCommand: Command = {
type: "Sequential",
commands: [
this.getMoveAnimation(0, -200, "animalImage"),
this.getMoveAnimation(-200, 0, "animalImage"),
],
};
return jumpCommand;
}
getMoveAnimation(fromY: number, toY: number, componentId: string): Command {
const moveAnimation: Command = {
type: "AnimateItem",
duration: 500,
value: [
{
property: "transform",
from: [
{
translateY: fromY,
},
],
to: [
{
translateY: toY,
},
],
},
],
componentId: componentId,
};
return moveAnimation;
}
}
// AnimalApl.tsx
//...
export class AnimalApl extends React.Component<{ animal: Animal }> {
render() {
return (
<APL token="animalAplToken">
<MainTemplate>
<Frame width="100vw" height="100vh" backgroundColor="rgb(22,147,165)">
<Container
width="100vw"
height="100vh"
alignItems="center"
justifyContent="spaceAround"
>
<Text text={this.props.animal.title} fontSize="50px"
color="rgb(251,184,41)"
/>
<Image
id="animalImage"
source={this.props.animal.image}
height="60vh"
width="30vh"
scale="best-fit"
/>
</Container>
</Frame>
</MainTemplate>
</APL>
);
}
}
In case of using User-defined Commands rather than defining a dynamic Command and using it inside Jsx as a property, commands
property must be provided to APL component: <APL commands={commands_map}>
// MyApl.tsx
//...
const setValueCommand: Command = {
type: "SetValue",
property: "opacity",
value: "${opacity}"
}
const myCommands = {
changeOpacity: {
parameters: [
"opacity"
],
command: setValueCommand
}
}
export class MyApl extends React.Component<{ animal: Animal }> {
render() {
const onPress = {
type: "changeOpacity",
opacity: 0.5,
};
return (
<APL commands={myCommands}>
<MainTemplate>
<TouchWrapper onPress={[onPress]}>
<Image
id="animalImage"
source={this.props.animal.image}
height="60vh"
width="30vh"
scale="best-fit"
/>
</TouchWrapper>
</MainTemplate>
</APL>
);
}
}
Parent-child component relationship for Responsive Templates(AlexaImageList, AlexaLists, AlexaPaginatedList, AlexaTextList) is supported by JSX for APL.
// MyImageList.jsx
import { APL, MainTemplate } from "ask-sdk-jsx-for-apl";
import { AlexaImageList, AlexaImageListItem } from "ask-sdk-jsx-for-apl/alexa-layouts";
export class MyImageList extends React.Component {
render() {
return (
<APL>
<MainTemplate>
<AlexaImageList imageBlurredBackground={true}>
<AlexaImageListItem primaryText="First list item" secondaryText="First Secondary text" imageSource="https://d2o906d8ln7ui1.cloudfront.net/images/md_gruyere.png" />
<AlexaImageListItem primaryText="Second list item" secondaryText="Second Secondary text" imageSource="https://d2o906d8ln7ui1.cloudfront.net/images/md_gruyere.png" />
</AlexaImageList>
</MainTemplate>
</APL>
);
}
}
JSX for APL has one to one property mapping with APL Document Properties inside APL
component. You can check some of the example use cases below.
Skill developers may need to use Resources and/or Styles rather than binding data to a component on runtime utilizing React Props or React Context.
In that case resources
and/or styles
must be provided to APL
component.
// MyStylesAndResourcesApl.jsx
...
const resources = [
{
colors: {
"MyBlue": "#0022f3"
}
}
];
const styles = {
textJsxStyle: {
values: [
{
fontSize: 24,
color: "@MyBlue"
}
]
}
}
export class MyStylesAndResourcesApl extends React.Component {
render() {
return (
<APL resources={resources} styles={styles}>
<MainTemplate>
<Text style="textJsxStyle" text="Testing Jsx Styles" />
</MainTemplate>
</APL>
);
}
}
Skill developers may need to use APL Data Sources and use them via APL Data-Binding rather than binding data to a component on runtime utilizing React Props or React Context.
When there is a need of using APL Data Source, data source object must be provided to dataSources
property of APL
component.
// CheeseApl.jsx
...
export class CheeseApl extends React.Component {
render() {
const cheeseData = {
gruyere: {
title: "Gruyere",
image: "https://d2o906d8ln7ui1.cloudfront.net/images/md_gruyere.png"
}
};
return (
<APL dataSources={cheeseData}>
<MainTemplate parameters={["payload"]}>
<Frame width="100vw" height="100vh" backgroundColor="rgb(22,147,165)">
<Container width="100vw" height="100vh" alignItems="center" justifyContent="spaceAround">
<Text text="${payload.gruyere.title}" fontSize="50px" color="rgb(251,184,41)" />
<Image source="${payload.gruyere.image}" height="60vh" width="30vh" scale="best-fit" />
</Container>
</Frame>
</MainTemplate>
</APL>
);
}
}
The defined React components inside a skill can be used multiple times to serve different content. They basically serve like APL Layout.
There can be times when skill developers want to reduce directive payload size and using APL Layout can be a solution for this situation.
The solution can be achieved by using AplLayout
component inside a JSX template.
// MySkillApl.jsx
import * as React from "react";
import omit from 'lodash/omit';
import { APL, MainTemplate, AplLayout, Container, Text } from "ask-sdk-jsx-for-apl";
//Create custom layout.
const MyTextLayout = (props) => {
return (
<>
<AplLayout name="MyTextLayout">
<Container width="100vw" height="100vh" direction="column">
<Text text={props.text} />
</Container>
</AplLayout>
</>
);
}
}
//Use the defined custom layout inside APL.
export class MySkillApl extends React.Component {
render() {
return (
<APL>
<MainTemplate>
<MyTextLayout text="Testing Jsx Layouts" />
</MainTemplate>
</APL>
);
}
}
If you are coding with typescript, you can even leverage IDE auto-completion ability by defining props for your Layout component.
// MyTextLayout.tsx
...
//Layout properties
interface MyTextProps {
text: string;
}
//Create custom layout component.
class MyTextLayout extends React.Component<MyTextProps> {
public render() {
return (
<>
<AplLayout name="MyTextLayout">
<Container width="100vw" height="100vh" direction="column">
<Text text={this.props.text} />
</Container>
</AplLayout>
</>
);
}
}
To use VectorGraphic component with JSX for APL, graphics
property must be provided inside APL
component following Vector Graphic Format guideline.
// MyGraphicsApl.jsx
import * as React from "react";
import { APL, MainTemplate, VectorGraphic } from "ask-sdk-jsx-for-apl";
const graphics = {
parameterizedCircle: {
type: "AVG",
version: "1.0",
height: 100,
width: 100,
parameters: [
{
name: "circleColor",
type: "color",
default: "black"
},
{
name: "circleBorderWidth",
type: "number",
default: 2
}
],
items: [
{
type: "path",
pathData: "M25,50 a25,25 0 1 1 50,0 a25,25 0 1 1 -50,0",
stroke: "${circleColor}",
strokeWidth: "${circleBorderWidth}",
fill: "none"
}
]
}
}
export class MyGraphicsApl extends React.Component {
render() {
return (
<APL graphics={graphics}>
<MainTemplate>
<VectorGraphic source="parameterizedCircle" width={100} height={100}
circleColor="red" circleBorderWidth={15} />
</MainTemplate>
</APL>
);
}
}