amp-bind

Edit on Github
Open in Playground

Introduction

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

It works by mutating elements in response to user actions via data binding and JS-like expressions.

For example, amp-bind can be used to:

  • Link two amp-carousel components into an image gallery with a scrolling thumbnail list.
  • Create an e-commerce product page where the UI changes if the user's currently selected item is not available.

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>

Configuring initial state

amp-bind works by binding elements to an implicit JSON "state". This state can be initialized with one or more <amp-state> components.

Example

<amp-state id="allAnimals">
  <script type="application/json">
    {
      "currentAnimal": "dog",
      "dog": {
        "imageUrl": "/img/Border_Collie.jpg",
        "videoUrl": "/video/dog-video.mp4",
        "style": "greenBackground",
        "iframeUrl": "https://player.vimeo.com/video/183849543"
      },
      "cat": {
        "imageUrl": "/img/cat-looking-up-300x200.jpg",
        "videoUrl": "/video/cat-video.mp4",
        "style": "redBackground",
        "iframeUrl": "https://player.vimeo.com/video/185199565"
      }
    }
  </script>
</amp-state>

Referencing implicit state in binding expressions is done using dot or bracket syntax, starting with the <amp-state> element's id, e.g. allAnimals.dog.imageUrl or allAnimals['cat']['imageUrl'].

  • The contents of <amp-state> must be a single JSON script.
  • Similar to the <amp-analytics> config, placing metadata at the end of the document helps the page display more quickly.

Creating data bindings

A data binding is a link between an HTML element and an expression. When the expression's value changes, the element is updated with that value.

Three types of element state can be bound:

  1. text for textContent
  2. class for CSS classes
  3. Element-specific attributes, for example, the src attribute for amp-img or amp-video.

To create a binding, create a new attribute on an element with the syntax: [attribute]="expression"

Binding Text

The text of this <p> will change when the currentAnimal variable changes. currentAnimal is an uninitialized implicit state variable, which will be set when a state change is triggered (see below). A state variable default can be specified using amp-state.

Example

This is a dog.

<p [text]="'This is a ' + allAnimals.currentAnimal + '.'">This is a dog.</p>

Binding Styles

Styling can be changed by applying CSS classes, which will override all the element's style classes. In the next example, allAnimals is the id of the amp-state component above and we use bracket notation because currentAnimal is a variable.

Example

Each animal has a different background color.

<p [class]="allAnimals[allAnimals.currentAnimal].style"
  class="greenBackground">Each animal has a different background color.</p>

Binding Attribute Values

Various AMP components support binding attribute values with amp-bind:

amp-img URLs can be changed.

Example

<amp-img id="amp-img"
  src="/img/Border_Collie.jpg"
  layout="responsive"
  width="300"
  height="200"
  [src]="allAnimals[allAnimals.currentAnimal].imageUrl">
</amp-img>

The src URL for videos embedded with amp-video can be changed. At the moment, among all the AMP video components, amp-video and amp-youtube support amp-bind.

Example

<amp-video id="amp-video"
  src="/video/dog-video.mp4"
  layout="responsive"
  [src]="allAnimals[allAnimals.currentAnimal].videoUrl"
  width="300"
  height="170"
  autoplay
  controls></amp-video>

The src for iframes embedded with amp-iframe can be changed.

Example

<amp-iframe id="amp-iframe"
  title="Cute dog video"
  width="500"
  height="281"
  layout="responsive"
  sandbox="allow-scripts allow-same-origin allow-popups"
  allowfullscreen
  frameborder="0"
  src="https://player.vimeo.com/video/183849543"
  [src]="allAnimals[allAnimals.currentAnimal].iframeUrl">
</amp-iframe>

Triggering updates

Data bindings are re-evaluated when implicit state changes. Implicit state can be updated via the AMP.setState action in response to an event. Read more about actions and events.

You can use various events on input/form elements as well as AMP elements to trigger AMP.setState. For a more in-depth example using multiple events, see autosuggest.

Example

Hello !
<div class="ampstart-input m1">
  <input id="name-input"
    placeholder="Enter a name"
    on="input-throttled:AMP.setState({name: event.value})">
  <div class="mt1">
    Hello
    <em [text]="name"></em>!
  </div>
</div>

Setting new state

The setState action takes an object that tells it what state to update. A few notes about the update object:

  • AMP.setState merges the new state into the existing implicit state
  • The top level property names correspond to the id of an <amp-state>
    • In the following example, we are updating the state held by <amp-state id="allAnimals">
    • You can update state without an asociated <amp-state> Element, as in the previous example
  • Existing state can be overwritten, including state initialized by amp-state components

Example

<button class="ampstart-btn caps m1"
  on="tap:AMP.setState({allAnimals: {currentAnimal: 'cat'}})">
  Set to Cat
</button>

Configuring initial state remotely

You can also bind to the amp-state src to provide data remotely. The remote data returned from this fetch is merged into the amp-state json data, for example:


  {"currentAnimal": "bird",
    "dog": {
      "imageUrl": "/img/Border_Collie.jpg",
      "videoUrl": "/video/dog-video.mp4",
      "style": "greenBackground",
      "iframeUrl": "https://player.vimeo.com/video/183849543"
    },
    "bird": {
      "imageUrl": "/img/bird.jpg",
      "videoUrl": "/video/bird-video.mp4",
      "style": "yellowBackground",
      "iframeUrl": "https://player.vimeo.com/video/151358139"
    }
  }

Example

<amp-state id="remoteAnimals"
  [src]="remoteAnimalUrl">
  <script type="application/json">
    {
      "currentAnimal": "dog",
      "dog": {
        "imageUrl": "/img/Border_Collie.jpg",
        "videoUrl": "/video/dog-video.mp4",
        "style": "greenBackground",
        "iframeUrl": "https://player.vimeo.com/video/183849543"
      }
    }
  </script>
</amp-state>

Load state remotely

By setting the src attribute of the amp-state, new data is fetched and merged into the amp-state.

Example

    <button class="ampstart-btn caps m1"
      on="tap:AMP.setState({remoteAnimalUrl: '/json/amp-bind-data-new-state.json?RANDOM'})">
      Set to Bird
    </button>

Similarly to the samples above, you can bind to text, ...

Example

This is a dog.

    <p [text]="'This is a ' + remoteAnimals.currentAnimal + '.'">This is a dog.</p>

... bind to amp-img src ... and all the components described here

Example

<amp-img src="/img/Border_Collie.jpg"
  layout="responsive"
  width="300"
  height="200"
  [src]="remoteAnimals[remoteAnimals.currentAnimal].imageUrl">
</amp-img>

Hiding and showing things

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.

Example

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

amp-lightbox can be opened as a result of amp-bind evaluation.

Example

  <amp-lightbox id="my-lightbox"
    [open]="showLightbox"
    layout="nodisplay">
    <div class="lightbox"
      on="lightboxClose: AMP.setState({showLightbox: false}); tap:my-lightbox.close"
      role="button"
      tabindex="0">
      <h1>Hello World!</h1>
    </div>
  </amp-lightbox>
  <button class="ampstart-btn caps m1"
    on="tap:AMP.setState({showLightbox: true})">
    OPEN LIGHTBOX
  </button>