DEV Community

loading...

Alexa Presentation Language, Part 1

Alexander Martin
・9 min read

In the last episode of TwoVoiceDevs, Allen Firstenberg and Stuart Pocklington talked about APL. During the interview, comparisons between APL and other technologies like HTML, CSS, and JavaScript were made over and over again. Inspired by this episode, I decided to write a blog article about it.

Before we start, what is APL? APL is the abbreviation of Alexa Presentation Language and at its core it consists of several parts. One part is the APL document parsing.

By the way, the source code of APL is available on Github.

So, if there is an APL document parser, there must be an APL document - so what exactly is an APL document? The official APL documentation describes the APL document like this:

"An APL document is a JSON structure that defines a template to display on the viewport. The document controls the overall structure and layout of the visual response."

So we can say an APL document is like an HTML document, and the Alexa Presentation Language is like the Hypertext Markup Language a markup language that is used to structure electronic documents that include texts, images, and other content.

Furthermore, the official documentation says:

"An APL document combines multiple parts".

Let's look at the individual parts in detail.

APL Component

"An APL component is a primitive UI element that displays on the viewport, such as a simple text box. Components are the basic building blocks for constructing a document."

We could compare an APL component to a web component. Web components are a set of functions that provide a standard component model for the web and allow encapsulation and interoperability of individual HTML elements. In the end, we can apply this description 1:1 to our APL component. By design, APL provides a set of native APL components, these include: Text, Image, Container, ScrollView and a few others, we will go into more detail later.

APL Layouts

"A layout combines components into a reusable template and gives it a name. You can then place the layout in your document by referencing its name. Referencing a name results in a more modular design that is easier to maintain."

Again, we can compare an APL layout to a web component, because there are two types. In this case, however, we're talking about a custom, reusable web component that encapsulates other web components. Which simply means that our APL layout extends the standard APL component model with custom components.

Styles

"A style assigns a name to a set of visual characteristics, such as color and font size. You can then assign the style to a component. Use styles to keep your visual design consistent."

The concept of styles can be compared to Cascading Style Sheets from the web, it provides a way to manage the look of an APL component in one central place. Additionally, as in the web context, we have the possibility to define different styles for individual states of our component. In the web we know states like "hover", "focus", "active" and so on, also these states exist in the context of APL, partly the names are exactly the same as in the case of "hover", partly they differ, in the case of "focus", we speak in the APL context of "focused".

Resource

"A resource is a named constant you can use rather than hardcoding values. For example, you could create a resource called myRed that defines a particular shade of red, and then use that resource name to specify a color for a component."

Resources, or rather constants, are commonly used in development. Constants are usually maintained in a central place, are immutable, and are available at any point in my code.

Package

"A package bundles together all of the above elements so that you can use them across multiple APL documents. You import the package into your document."

The concept of packages is not a new invention of APL. In the context of software development, there are various names for them, dependencies, packages, modules. No matter what you call them, the underlying principle is always the same. I can use packages to add functionality to my application, I can offload things to reuse them or to structure my application.

Amazon itself provides 3 such packages.

alexa-viewport-profiles

There exists a wide range of devices on the market that support APL, whether from Amazon itself or from a third-party vendor. In this package, Amazon provides resources that group the different devices by their characteristics and combine them into a viewport profile. I would highly recommend everyone to generally work with these profiles as they ensure that my APL document will look the same on all devices associated with that profile.

alexa-styles

This package provides different resources and styles to make sure my layout works for different viewports. Let's take the example of font size, on a device with a smaller screen I obviously can't use the same font size as on a device with a larger screen. The alexa-styles package provides predefined font size names (small, medium, large) that vary depending on the device. It also provides spacing, colors and other things.

alexa-layouts

We learned earlier that APL layouts complement the standard component model, as the name of the package suggests that's exactly what happens here too. Amazon provides so-called responsive templates and components with this package, ensuring that the component works accross all viewports. These responsive components include things like a header or footer, a background image, a progress bar and many more, responsive layouts include things like a page by page list or a grid list and many others.

You're probably asking yourself now, why should I break up my APL document into packages myself? There are some advantages why you should do it:

  • your skill will probably end up consisting of more than just one APL document, using a package you can share APL layouts, styles or anything else between these documents.

  • the client, in this case the device, can cache a package which means that during the lifetime of the skill session the device has to request less data

  • there is a response size limit, which means that the size of your skill response may not exceed a certain limit, depending on the complexity of your APL document you can reach this limit very quickly. However, you can work around this by using packages.

Okay, so at this point we have learned the parts of our APL document. Now let's dive into the components, as we said earlier. To give you a feeling for what the component does I will show some example definitions and the corresponding HTML equivalent.

The Text component

APL:

{
  "type": "Text",
  "text": "Hello world."
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<p>Hello world.</p>
Enter fullscreen mode Exit fullscreen mode

The Image component

APL:

{
  "type": "Image",
  "source": "https://sld.tld/image.png",
  "width": 100,
  "height": 100
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<img src="https://sld.tld/image.png" width="100" height="100" />
Enter fullscreen mode Exit fullscreen mode

The ScrollView component

APL:

{
  "type": "ScrollView",
  "height": "50px",
  "width": "100px",
  "item": {
    "type": "Image",
    "source": "https://sld.tld/image.png",
    "width": 100,
    "height": 100
  }
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<div style="overflow: auto; width: 100px; height: 50px;">
  <img src="https://sld.tld/image.png" width="100" height="100" />
</div>
Enter fullscreen mode Exit fullscreen mode

The Container component

At this point I would like to mention that APL uses the flexbox concept.

APL:

{
  "type": "Container",
  "alignItems": "center",
  "justifyContent": "center",
  "width": "100vw",
  "height": "100vh",
  "items": [{
    "type": "Text",
    "text": "Hello world."
  }]
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<div style="display: flex; align-items: center; justify-content: center; width: 100vw; height: 100vh;">
  <p>Hello world.</p>
</div>
Enter fullscreen mode Exit fullscreen mode

In HTML there are different types of elements, these different types also exist for APL components.
They are divided into "Base Components", "Actionable Components", "Touchable Components" and "Multi-child Components".

Let's start with the Base components.

Base components

A component is a primitive element that displays on the viewport.

Texts, images, videos are meant here. The other component types derive from the base component.

Actionable components

An actionable component can directly receive input from touch, cursor, or keyboard events.

So with the help of an Actionable component, I have the ability to respond to user input directly in my APL document. For example, I can send an entered text to my skill backend.

The EditText component

APL:

{
  "type": "EditText",
  "onFocus": [],
  "onBlur": []
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<input type="text" onfocus="" onblur="" />
Enter fullscreen mode Exit fullscreen mode

Touchable components

A touchable component receives input from touch or pointer events and invokes handlers to support custom touch interaction behavior.

Touchable components are an extension of Actionable components. As the name implies, the Touchable component gives me access to user input that is triggered by touch, for example. I also have the ability to provide more complex interaction capabilities with my APL document.

The TouchWrapper component

APL:

{
  "type": "TouchWrapper",
  "onDown": [],
  "onUp": [],
  "onMove": [],
  "item": {
    "type": "Text",
    "text": "TouchWrapper Sample"
  }
}
Enter fullscreen mode Exit fullscreen mode

HTML:

<button ontouchstart="" ontouchend="" ontouchmove="">
  <p>TouchWrapper Sample</p>
</button>
Enter fullscreen mode Exit fullscreen mode

Last but not least, the Multi-child components.

Multi-child components

A multi-child component is an abstract component that arranges and displays multiple child components

Multi-child components offer the possibility of the so-called Data Array Inflation method. You are probably wondering what the Data Array Inflation method is? Using the Data Array Inflation method we have the possibility to bind an array of values to our component, in this case we can consider the child items of our component as a template, while processing our document for each item in our array the defined template will be applied. Let me try to explain with javascript and HTML what exactly happens.

APL:

{
  "type": "Container",
  "data": [{ "name": "Foo" }, { "name": "Bar" }, { "name": "Baz" }],
  "items": [{
    "type": "Text",
    "text": "I'm ${data.name} and the ${index + 1}${index == 0 ? 'st' : '${index == 2 ? 'rd' : 'nd'}'} child in the array."
  }]
}
Enter fullscreen mode Exit fullscreen mode

JavaScript/HTML:

[{ name: 'Foo' }, { name: 'Bar' }, { name: 'Baz' }].reduce((items, data, index) => {
  return items.concat(`<p>I'm ${data.name} and the ${index + 1}${index == 0 ? 'st' : `${index == 2 ? `rd` : `nd`}`} child in the array.</p>`);
}, []).join('\n');
Enter fullscreen mode Exit fullscreen mode

Note: this example is highly simplified and does not support the correct spelling of the numbers st, nd, rd, th.

Once we run our JavaScript snippet, we end up with the following output, the same also applies to APL.

<p>I'm Foo and the 1st child in the array.</p>
<p>I'm Bar and the 2nd child in the array.</p>
<p>I'm Baz and the 3rd child in the array.</p>
Enter fullscreen mode Exit fullscreen mode

Wonder why I used the reduce and not the map function for this example? APL offers besides the Data Array Inflation method the so called Conditional Component Inflation, but what does that mean? It's very simple, I can set conditions that in the end influence which elements should be rendered and which not.

Let me demonstrate using our previous example.

APL:

{
  "type": "Container",
  "data": [{ "name": "Foo" }, { "name": "Bar" }, { "name": "Baz" }],
  "items": [{
    "when": "${data.name == 'Foo' || data.name == 'Baz'}",
    "type": "Text",
    "text": "I'm ${data.name} and the ${index + 1}${index == 0 ? 'st' : '${index == 2 ? 'rd' : 'nd'}'} child in the array."
  }]
}
Enter fullscreen mode Exit fullscreen mode

JavaScript/HTML:

[{ name: 'Foo' }, { name: 'Bar' }, { name: 'Baz' }].reduce((items, data, index) => {
  if (data.name == 'Foo' || data.name == 'Baz') {
    return items.concat(`<p>I'm ${data.name} and the ${index + 1}${index == 0 ? 'st' : `${index == 2 ? `rd` : `nd`}`} child in the array.</p>`);
  }

  return items;
}, []).join('\n');
Enter fullscreen mode Exit fullscreen mode

If we run our JavaScript snippet again, we get a different return value, this is because we only consider the elements in our data array that have the name "Foo" or "Baz".

<p>I'm Foo and the 1st child in the array.</p>
<p>I'm Baz and the 3rd child in the array.</p>
Enter fullscreen mode Exit fullscreen mode

And this is how Conditional Component Inflation works.

I could go on for hours, but for now I would leave it at that. What do you think? Did I confuse you more with this article than you already were? Did the article help you understand APL better? Let me know, leave a Like if you want to read more articles like this. Write me on Twitter, or use the official Alexa Slack Workspace to meet me, or other Alexa/APL developers. And if you want to find out what's possible with APL, check out APL Ninja.

Learn more:

Discussion (1)

Collapse
aruntalkstech profile image
Arun Singh

Nice article! I especially like the direct comparisons between APL primitives and the HTML/JS equivalents. Looking forward to the next part.