Skip to content
This repository has been archived by the owner on May 10, 2023. It is now read-only.

Advanced Topics

Umur Kontacı edited this page Oct 23, 2020 · 23 revisions

1. APL Commands

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>
    );
  }
}

2. Responsive Templates

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>
    );
  }
}

3. APL Document Properties

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.

3.1. APL Resources & Styles

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>
    );
  }
}

3.2. APL Data Sources

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>
        );
    }
}

3.3. APL Layout

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>
    </>
    );
  }
}

3.4. VectorGraphic

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>
    );
  }
}