AMP-Bind for E-Commerce: Interactive Carousels

Bringing flexible interactivity to your AMP pages

The amp-bind component allows you to build custom, stateful interactivity and provide responsive interactions without sacrificing the high performance that makes AMP so powerful. In this article, we’ll be looking at a common component found in e-commerce sites: carousel slide-indicators.

How to build slide indicators for image carousels with AMP

Amp-carousel doesn’t come with built-in, slide-indicator functionality but fortunately amp-bind enables you to create your own, no matter the desired style.

First step is to create the amp-state

<amp-state id="slideIndicator">
<script type="application/json">
{
"currSlide": 0
}
</script>
</amp-state>
view raw amp-state.html hosted with by GitHub

We can think of this amp-state as a JSON structure where the id of the amp-state is the top-level member. So it would look like:

{
"slideIndicator":{
"currSlide": 0
}
}

This amp-state will keep track of the state of the slide indicator. Amp-carousel is zero-based indexed, so the first slide will be slide 0.

Next, we create the amp-carousel

The key attribute in this example is the carousel type attribute. Considering we’re making a slide indicator, we’ll need to work with a carousel that utilizes a slide method.

But wait, we also need to modify the amp-state from within the amp-carousel. When the slide is changed manually, the amp-state must be updated accordingly. Let’s add this on attribute to the carousel:

on=”slideChange:AMP.setState({slideIndicator: {currSlide: event.index}})”

The on attribute contains an event, target, and an action. “slideChange” is the event in this case, “AMP” is the target, and “setState()” is the action. The target can either be the id of an element or “AMP,” which is a special, generic target. The argument we’re giving “setState()” is the new state of the amp-state we created earlier. Now every time the carousel is manually changed, the amp-state JSON structure will change to:

{
"slideIndicator":{
"currSlide": event.index
}
}

Where event.index is the slide number. event.index is the data that can be passed for this specific event for amp-carousels with type=”slides”. Now the amp-carousel will look like:

Last step is the slide indicator

Great. Finally, let’s create the slide indicator. It’ll be in the form of a group of spans that can be styled to our liking.

<div class="indicator-container">
<span
[class]="'indicator '+(slideIndicator.currSlide == 0 ? 'curr-indicator' : '')"
class="indicator curr-indicator">
</span>
<span [class]="'indicator '+(slideIndicator.currSlide == 1 ? 'curr-indicator' : '')"></span>
<span [class]="'indicator '+(slideIndicator.currSlide == 2 ? 'curr-indicator' : '')"></span>
</div>

Each span has a class that is bound to an expression. In this example, the class of each span will either be “curr-indicator” or empty depending on the result of their respective expression.

Notice how the first indicator is defaulted to include the class “curr-indicator”. That’s because we initially defined the amp-state to start with currIndicator = 0, so the expression (slideIndicator.currSlide == 0 ? ‘curr-indicator’ :  ”) will be the only expression evaluating to true. We can style each class any way we choose. The class “curr-indicator” will obviously be unique to the generic “indicator” class.

And that’s it! We now have a working example of slide indicators that change their CSS depending on the amp-state.

How about some added functionality?

What if we want the user to be able to tap the indicators to allow them to quickly go from slide 0 to slide 2? As you may have guessed, we’re going to use the on attribute for this functionality.

Every time we tap an indicator (the action), we want the amp-carousel (the target) to go to the corresponding slide (the event). The attribute will look like:

on=”tap:slideIndicator.goToSlide(index=#)”

where # is the slide number we want to go to. So now the indicators will look like:

<div class="indicator-container">
<span
[class]="'indicator '+(slideIndicator.currSlide == 0 ? 'curr-indicator' : '')"
class="indicator curr-indicator"
on="tap:slideIndicator.goToSlide(index=0)">
</span>
<span
[class]="'indicator '+(slideIndicator.currSlide == 1 ? 'curr-indicator' : ' ')"
on="tap:slideIndicator.goToSlide(index=1)">
</span>
<span
[class]="'indicator '+(slideIndicator.currSlide == 2 ? 'curr-indicator' : '')"
on="tap:slideIndicator.goToSlide(index=2)">
</span>
</div>

And we can’t forget to change the state of the amp-state. To add multiple events to a single action, we just need to separate them with a comma.

<div class="indicator-container">
<span
[class]="'indicator '+(slideIndicator.currSlide == 0 ? 'curr-indicator' : '')"
class="indicator curr-indicator"
on="tap:slideIndicator.goToSlide(index=0),AMP.setState({slideIndicator: {currSlide:0}})">
</span>
<span
[class]="'indicator '+(slideIndicator.currSlide == 1 ? 'curr-indicator' : ' ')"
on="tap:slideIndicator.goToSlide(index=1),AMP.setState({slideIndicator: {currSlide: 1}})">
</span>
<span
[class]="'indicator '+(slideIndicator.currSlide == 2 ? 'curr-indicator' : '')"
on="tap:slideIndicator.goToSlide(index=2),AMP.setState({slideIndicator: {currSlide: 2}})">
</span>
</div>

And there you have it. A fully functioning carousel with an interactive slide indicator. It’s really that easy with AMP.