Skip to content
This repository has been archived by the owner on May 10, 2023. It is now read-only.
yusufsipahi edited this page Jul 17, 2020 · 19 revisions

Welcome To Ask-Sdk-Jsx-For-Apl

Introduction

This project ask-sdk-jsx-for-apl is a React-based APL templating framework that allows developers to define APL document within the code. By using the React-style JSX/TSX file format, developers can include Vesper components as XML-style definition for the APL and shorten the APL definition code length, making the development more manageable.

Why should you use Ask-Sdk-Jsx-For-Apl?

Ask-Sdk-Jsx-For-Apl has many benefits over standard JSON-based APL templates, such as the following aspects.

1. Code-First Approach

Ask-Sdk-Jsx-For-Apl uses React-based components to define each APL document. By using JSX files to include XML-based APL document declaration and defining the document in an encapsulated React component, skill developers can simply import and include the component into the response as a natural Javascript/Typescript code.

  • Following code shows how a Ask-Sdk-Jsx-For-Apl APL document can naturally 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;
        return responseBuilder
            .addDirective(new AplDocument(<LaunchApl />).getDirective())
            .speak("Welcome to my first Vesper 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';

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

2. Dynamic APL Response

By leveraging React's component render lifecycle, ask-sdk-jsx-for-apl can dynamically change the custom component's behaviour and minimize the returned APL document size.

For example, suppose a chess game skill is developed using APL. In traditional JSON-based APL definition, the positions and padding for each board slot must be defined most likely in datasource section and each piece will need to refer to the spacing definition in the datasource. With ask-sdk-jsx-for-apl, the complexity is reduced since the spacing calculation is done on the Lambda, freeing up the APL document for more details.

  • The following example shows a usage of dynamic ask-sdk-jsx-for-apl 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';
        }
        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() {
      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>
      );
    }
}

2. Readability, Reusability and Reliability

In the traditional JSON-based APL, a reuse of components can get complicated. Without use of layouts, multiple copies of same structure can exist and code change management can get very complex. With ask-sdk-jsx-for-apl, however, the reusability issue is resolved by simply creating new instances of a defined component class, making changes very fast and reliable.

  • 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;
Clone this wiki locally