AMP Conf 2019. April 17/18. Tokyo.
AMP
  • websites

amp-bind

Introduction

amp-bind allows you to add custom interactivity to your pages beyond using AMP's pre-built components.

Setup

Import the amp-bind component in the header.

<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

Basic usage

With amp-bind, you can update element attributes and values via bindings. Here we update the hidden attribute based on a state variable called hideGreeting. On button press, we use the AMP.setState() action to update the state.

<div>
  <div hidden
    [hidden]="hideGreeting">Hello World</div>
  <button on="tap:AMP.setState({ hideGreeting: false })">Show greeting</button>
</div>

Binding text

You can dynamically change the text value of an element by declaring a binding to the [text] attribute.

Hello World
<div>
  <div>Hello
    <span [text]="myText">World</span>
  </div>
  <button on="tap:AMP.setState({ myText: 'AMP' })">Change text</button>
</div>

Binding CSS classes

You can dynamically change the CSS classes of an element by adding a binding to the [class] attribute.

Hello World
<div>
  <div class="background-red"
    [class]="myClass">Hello World</div>
  <button on="tap:AMP.setState({ myClass: 'background-green' })">Change class</button>
</div>

Binding width and height

Basic element properties such as width and height can be updated as well.

<div>
  <amp-img src="https://unsplash.it/400/200"
    width="200"
    [width]="myImageDimension.width"
    height="100"
    [height]="myImageDimension.height">
  </amp-img>
  <button on="tap:AMP.setState({
                      myImageDimension: {
                        width: 400,
                        height: 200
                      }
                    })">
    Change size
  </button>
</div>

Hiding & showing

This sample toggles the visibility of two divs based on a input selection. AMP provides the hidden attribute, which we use to hide and show the two divs. Some elements, such as the select element, fire events we can use to update state.

  <div>
    <select on="change:AMP.setState({ option: event.value })">
      <option value="0">No selection</option>
      <option value="1">Option 1</option>
      <option value="2">Option 2</option>
    </select>
    <div hidden
      [hidden]="option != 1">
      Option 1
    </div>
    <div hidden
      [hidden]="option != 2">
      Option 2
    </div>
  </div>

Initializing state

The initial value of an amp-state variable is null. However, bindings are not evaluated on page load, but on subsequent user actions. This can lead to unwanted side effects if amp-state variables are not correctly initialized.

In this sample, both greetings bind to different amp-state variables. One is initialized via an JSON string inside an amp-state element, the other is not. When the user triggers an AMP.setState(...) action, both bindings will be evaluated resulting in the first binding displaying a null value.

1. Hello World
2. Hello World
<div>
  <amp-state id="myText">
    <script type="application/json">
      "World"
    </script>
  </amp-state>
  <div>1. Hello
    <span [text]="undefinedText">World</span>
  </div>
  <div>2. Hello
    <span [text]="myText">World</span>
  </div>
  <button on="tap:AMP.setState({ myText: 'AMP' })">Change state</button>
</div>

Remote state

The amp-state element can pull in state from a remote JSON endpoint using the src attribute. Here we bind an amp-list's [src] attribute to amp-state data returned by an JSON endpoint. Also note how we calculate the amp-list's [height] based on the number of elements. The button triggers an empty AMP.setState() action to trigger amp-bind expressions to evaluate.

<div>
  <amp-state id="myRemoteState"
    src="/static/samples/json/websites.json"></amp-state>
  <amp-list layout="fixed-height"
    height="0"
    [height]="18 * myRemoteState.items.length"
    [src]="myRemoteState.items">
    <template type="amp-mustache">
      <div>
        <a href="{{url}}">{{title}}</a>
      </div>
    </template>
  </amp-list>
  <button on="tap:AMP.setState({})">Show websites</button>
</div>

Refresh state

amp-state supports the refresh action. This can be useful in many cases, for example the initial URL loading fails and you want the user to load the state again. Another common scenario is live content that needs to be updated, e.g. a sports live score. For a complete list of actions, see here. Clicking the button below will refresh and refetch the json in amp-state showing an updated time.

  <div>
    <amp-state id="time"
      src="/components/time"></amp-state>
    <button on="tap:time.refresh">
      Refresh
    </button>
    <div [text]="time"></div>
  </div>

Push State

AMP.pushState() writes state changes to the history. Navigating back, will restore the previous state.

To test this, increase the count and use your browser's back button to decrease the count.

Item 1
<div>
  <amp-state id="count">
    <script type="application/json">
      1
    </script>
  </amp-state>
  <div>Item
    <span [text]="count">1</span>
  </div>
  <button on="tap:AMP.pushState({ count: count + 1 })">Increase count</button>
</div>

Debounce input events

For text input, it's a good idea to debounce the input using the input-throttled event. For a more in-depth example, see autosuggest.

Hello ?
<div>
  <amp-state id="name">
    <script type="application/json">
      "?"
    </script>
  </amp-state>
  <input id="name-input"
    placeholder="Enter a name"
    on="input-throttled:AMP.setState({ name: event.value })">
  <div>Hello
    <span [text]="name">?</span>
  </div>
</div>

amp-bind-macro

amp-bind-macro makes it possible to reuse expressions across different actions.

The circle has an area of 0.
<div>
  <amp-bind-macro id="circleArea"
    arguments="radius"
    expression="3.14 * radius * radius" />
  <input type="number"
    min="0"
    max="100"
    value="0"
    on="input-throttled:AMP.setState({ radius: event.value })">
  <div>
    The circle has an area of
    <span [text]="circleArea(radius)">0</span>.
  </div>
</div>
Need further explanation?

If the explanations on this page don't cover all of your questions feel free to reach out to other AMP users to discuss your exact use case.

Go to Stack Overflow
An unexplained feature?

The AMP project strongly encourages your participation and contributions! We hope you'll become an ongoing participant in our open source community but we also welcome one-off contributions for the issues you're particularly passionate about.

Edit sample on GitHub
ampbyexample.com has moved to amp.dev (read the blog post) OK