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

Common Usage

Harry Park edited this page Jul 21, 2020 · 15 revisions

JSX for APL enables developers to use React components to model their responses. It does so by using JSX and React’s render lifecycle to attach Alexa.Presentation.APL.RenderDocument directive to the responseBuilder object.

APL Components Usage

Following code shows how the APL document can seamlessly fit into the LaunchIntent handler.

// MainSkill.js

import * as Alexa from 'ask-sdk';
import { LaunchIntentHandler } from './handlers/LaunchIntentHandler';

const builder = Alexa.SkillBuilders.custom();

const handler = builder.addRequestHandlers(
    new LaunchIntentHandler()
).lambda();
module.exports = { handler };
// handlers/LaunchIntentHandler.jsx

import * as Alexa from 'ask-sdk';
import * as React from "react";
import { LaunchApl } from './apl/LaunchApl';
import { AplDocument } from 'ask-sdk-jsx-for-apl';

class LaunchIntentHandler {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    }

    handle(handlerInput) {
        const responseBuilder = handlerInput.responseBuilder;
        //React Component render cycle is triggered via getDirective method of AplDocument
        return responseBuilder
            .addDirective(new AplDocument(<LaunchApl />).getDirective())
            .speak("Welcome to my first JSX for APL skill")
            .getResponse();
    }
};
module.exports = { LaunchIntentHandler };
// apl/LaunchApl.jsx

import * as React from 'react';
import { APL, MainTemplate, Frame, Container, Text } from 'ask-sdk-jsx-for-apl';

//React Component of LaunchIntent APL
export class LaunchApl extends React.Component {
    constructor(props) {
        super(props);
        this.launchMessage = 'Welcome to my first ask-sdk-jsx-for-apl skill!';
    }
    render() {
      return (
        <APL theme="dark">
          <MainTemplate>
            <Frame
              width="100vw"
              height="100vh"
              backgroundColor="rgb(22,147,165)"
            >
              <Container
                alignItems="center"
                justifyContent="spaceAround"
              >
                <Text
                  text={this.launchMessage}
                  fontSize="50px"
                  color="rgb(251,184,41)"
                />
              </Container>
            </Frame>
          </MainTemplate>
        </APL>
      );
    }
}

The following output shows the directive generated by using the above react component.
// Alexa.Presentation.APL.RenderDocument JSON Response
{
  "type": "Alexa.Presentation.APL.RenderDocument",
  "document": {
    "type": "APL",
    "version": "1.3",
    "import": [],
    "theme": "dark",
    "mainTemplate": {
      "parameters": [],
      "items": [
        {
          "items": [
            {
              "items": [
                {
                  "items": [],
                  "text": "Welcome to my first ask-sdk-jsx-for-apl skill!",
                  "fontSize": "50px",
                  "color": "rgb(251,184,41)",
                  "type": "Text"
                }
              ],
              "alignItems": "center",
              "justifyContent": "spaceAround",
              "type": "Container"
            }
          ],
          "width": "100vw",
          "height": "100vh",
          "backgroundColor": "rgb(22,147,165)",
          "type": "Frame"
        }
      ]
    }
  }
}

Parameter Binding

The following example shows a usage of dynamic APL Response to check for the viewport shape on runtime and service different width and height values for the APL documents.

// LaunchIntentHandler.jsx

import * as React from 'react';
import * as Alexa from 'ask-sdk-core';

import { LaunchApl } from './apl/LaunchApl';

class LaunchIntentHandler {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    }

    handle(handlerInput) {
        const responseBuilder = handlerInput.responseBuilder;
        const viewportShape = handlerInput.requestEnvelope.context.Viewport.shape;
        const aplParameters = {
          width: '100vw',
          height: '100vh'
        };
        if (viewportShape === 'ROUND') {
          aplParameters.width = '80vh';
          aplParameters.height = '80vh';
        }
        //Provide parameters to LaunchApl component.
        const LaunchAplDocument = new AplDocument(
            <LaunchApl aplParameters={aplParameters}/>
        ).getDirective();
        return responseBuilder
            .addDirective(LaunchAplDocument)
            .speak("Welcome to my first ask-sdk-jsx-for-apl skill")
            .getResponse();
    }
};
// apl/LaunchApl.jsx

import * as React from 'react';
import { APL, MainTemplate, Frame, Container, Text } from 'ask-sdk-jsx-for-apl';

export class LaunchApl extends React.Component {
    constructor(props) {
        super(props);
        this.launchMessage = 'Welcome to my first ask-sdk-jsx-for-apl skill!';
    }
    render() {
      //width and height parameters are provided from calling elements.
      return (
        <APL theme="dark">
          <MainTemplate>
            <Frame
              width={this.props.aplParameters.width}
              height={this.props.aplParameters.height}
              backgroundColor="rgb(22,147,165)"
            >
              <Container
                alignItems="center"
                justifyContent="spaceAround"
              >
                <Text
                  text={this.launchMessage}
                  fontSize="50px"
                  color="rgb(251,184,41)"
                />
              </Container>
            </Frame>
          </MainTemplate>
        </APL>
      );
    }
}

Create Reusable Components

The best way to make an APL component more maintainable and controllable is to break it into multiple components.

The following example shows the reuse of Container component to serve different content.

// apl/WorkoutApl.jsx

import * as React from 'react';
import { APL, Container, Image, Text } from 'ask-sdk-jsx-for-apl';
import WorkoutColumn from './workout-column-apl';

class WorkoutApl extends React.Component {
    private renderWorkoutPartsImage() { ... }
    private renderWorkoutStepsImages() { ... }
    private renderWorkoutStepsTexts() { ... }

    render() {
        return (
            <APL theme="dark">
                <MainTemplate>
                    <Container width="100%" height="80vh" direction="row">
                        <WorkoutColumn>
                            {
                                this.renderWorkoutPartsImage();
                            }
                        </WorkoutColumn>
                        <WorkoutColumn>
                            {
                                this.renderWorkoutStepsImages();
                            }
                        </WorkoutColumn>
                        <WorkoutColumn>
                            {
                                this.renderWorkoutStepsTexts();
                            }
                        </WorkoutColumn>
                    </Container>
                </MainTemplate>
            </APL>
        );
    }
}

export default WorkoutApl;
// apl/WorkoutColumn.jsx

import * as React from 'react';
import { Container } from 'ask-sdk-jsx-for-apl';

class WorkoutColumn extends React.Component {
    render() {
        return (
            <Container width="30%" height="100%"
                paddingBottom="16dp"
                paddingLeft="16dp"
                paddingRight="16dp"
                paddingTop="16dp"
                spacing="16dp">
                {this.props.children}
            </Container>
        );
    }
}

export default WorkoutColumn;