Расширенная интерактивность в AMP

Ускоренные мобильные страницы (AMP) — это инициатива с открытым исходным кодом, которая позволяет создавать веб-сайты и рекламные объявления, которые всегда будут быстрыми, красивыми и высокоэффективными.

Если вы новичок в AMP, рассмотрите краткий обзор этих ресурсов:

Мотивация

AMP поддерживает богатый динамический контент с компонентами пользовательского интерфейса, такими как карусели изображений и лайтбоксы . AMP также поддерживает несколько простых способов для одного компонента инициировать действие в другом компоненте с помощью AMP Actions .

Однако что, если я хочу:

  • Настроить компонент AMP?
  • Например, отобразите пользовательскую метку, показывающую текущий слайд и общее количество слайдов в карусели изображений.
  • Добавить поведение с отслеживанием состояния?
  • Например, отключите кнопку «Добавить в корзину», если выбранное пользователем количество товара превышает его текущую доступность.

Ранее реализовать подобные функции в AMP было сложно из-за отсутствия надежного канала связи между компонентами пользовательского интерфейса и невозможности иметь изменяемое общее состояние. Мы создали новый мощный компонент в AMP для решения этих задач.

<amp-bind>

<amp-bind> – это новый компонент AMP, предлагающий настраиваемую интерактивность посредством привязки данных и JS-подобных выражений. Эта лаборатория кода проведет вас через использование <amp-bind> для создания AMP-страницы с богатой настраиваемой интерактивностью.

Что вы будете строить

В этой лаборатории кода вы создадите страницу сведений о продукте электронной коммерции:

  • Используйте AMP HTML и компоненты AMP, чтобы создать веб-страницу с быстрым и удобным пользовательским интерфейсом.
  • Используйте <amp-bind> , чтобы добавить интерактивность между элементами
  • Используйте <amp-state> для получения дополнительных данных о продукте по запросу.

Что вы узнаете

  • Как использовать привязку данных и выражения для создания потрясающих интерактивных страниц AMP с помощью <amp-bind> !

Что вам понадобится

  • Браузер на ваш выбор
  • Текстовый редактор на ваш выбор
  • Node.js и NPM
  • Пример кода
  • Базовые знания HTML, CSS и JavaScript

Скачать код

Во-первых, загрузите начальный код для лаборатории кода в виде ZIP-файла:

Скачать

Или через гит:

git clone https://github.com/googlecodelabs/advanced-interactivity-in-amp.git

Установить зависимости

Разархивируйте файл архива (при необходимости) и перейдите в каталог. Установите зависимости, запустив npm install .

cd advanced-interactivity-in-amp-codelab
npm install

Запустите сервер разработки

Запустите сервер разработки с помощью node.js:

node app.js

Перейдите по адресу http://localhost:3000 в веб-браузере, чтобы увидеть работающую страницу AMP!

Шаблон AMP

AMP-страница — это HTML-страница с некоторыми ограничениями для надежной работы. Страницы AMP имеют специальную разметку, которая идентифицирует их как страницу AMP для поиска Google.

Базовая AMP-страница выглядит так:

<!doctype html>
<html amp>
 <head>
   <meta charset="utf-8">
   <link rel="canonical" href="hello-world.html">
   <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
   <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
   <script async src="https://cdn.ampproject.org/v0.js"></script>
 </head>
 <body>Hello World!</body>
</html>

Компоненты AMP

Наш начальный код ( static/index.html ) основан на базовой AMP-странице с ее содержимым (изображения, текст и т. д.), а также включает несколько компонентов AMP :

<script async custom-element="amp-carousel" src="https://cdn.ampproject.org/v0/amp-carousel-0.1.js"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"></script>
<script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
<script async custom-element="amp-selector" src="https://cdn.ampproject.org/v0/amp-selector-0.1.js"></script>

Компоненты AMP предлагают дополнительные функции и компоненты пользовательского интерфейса, которые добавляют интерактивные возможности страницам AMP. Стартовый код использует следующие компоненты AMP:

  • <amp-carousel>
  • Карусель изображений, которая отображает несколько видов продукта.
  • <amp-mustache>
  • Система шаблонов для рендеринга ответов сервера от amp-form.
  • <amp-form>
  • Добавляет специальные функции для элементов <form> , которые необходимы для AMP-страниц.
  • <amp-selector>
  • Предлагает семантический способ выбора одного или нескольких элементов группы элементов. Может использоваться как источник входного сигнала для формирования усилителя.

Базовая интерактивность

Стартовый код предлагает некоторую базовую интерактивность:

  • Карусель изображений ( <amp-carousel> ) отображает несколько видов продукта.
  • Товар можно добавить в корзину пользователя (через <amp-form> ), нажав кнопку «Добавить в корзину» внизу страницы.

Попробуйте прокрутить карусель изображений и нажать кнопку «Добавить в корзину».

Улучшить опыт

Стартовый код обеспечивает довольно простой пользовательский интерфейс. Есть несколько способов улучшить его:

  • Добавьте индикатор, отображающий текущий слайд и общее количество слайдов.
  • Когда пользователь выбирает другой цвет рубашки, измените карусель изображений, чтобы показать изображения рубашек выбранного цвета.

До введения компонента <amp-bind> добавление подобных функций было невозможно. Давайте попробуем на практике использовать <amp-bind> и добавим эти новые функции в наш пример кода!

Установите расширение <amp-bind>

<amp-bind> — это новый компонент AMP, который обеспечивает пользовательскую интерактивность посредством привязки данных и JS-подобных выражений. Чтобы использовать <amp-bind> , вы должны установить его на странице.

Откройте файл static/index.html и добавьте следующий скрипт в список компонентов AMP в разделе <head> страницы:

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

Добавьте скользящий индикатор

<amp-bind> работает путем привязки атрибутов элемента к пользовательским выражениям. Эти выражения могут ссылаться на «состояние» (изменяемые данные JSON). Мы можем инициализировать это состояние с помощью компонента <amp-state> , включенного в <amp-bind> .

Давайте инициализируем переменную состояния, чтобы отслеживать индекс текущего отображаемого слайда в карусели изображений. Откройте static/index.html и добавьте следующее в верхнюю часть <body> страницы (перед заголовком):

<amp-state id="selected">
  <script type="application/json">
    {
      "slide": 0
    }
  </script>
</amp-state>

Данные в элементах <amp-state> доступны по их связанному идентификатору. Например, мы можем сослаться на эту переменную с помощью следующего фрагмента выражения:

selected.slide // Evaluates to 0.

Затем давайте обновим эту переменную, когда пользователь меняет слайды в карусели, добавляя действие «вкл» к существующему элементу <amp-carousel>:

<amp-carousel type="slides" layout="fixed-height" height=250 id="carousel"
    on="slideChange:AMP.setState({selected: {slide: event.index}})">

Теперь при каждом изменении отображаемого слайда <amp-carousel> действие AMP.setState будет вызываться со следующим аргументом:

{
  selected: {
    slide: event.index
  }
}

Выражение event.index оценивается как новый индекс слайда, а действие AMP.setState() объединяет этот литерал объекта с текущим состоянием. Это заменяет текущее значение selected.slide значением event.index .

Далее давайте воспользуемся этой переменной состояния, которая отслеживает текущий отображаемый слайд, и создадим индикатор слайда. Найдите элемент индикатора слайда (ищите <!-- TODO: "Add a slide indicator" --> ) и добавьте следующие привязки к его дочерним элементам:

<!-- TODO: "Add a slide indicator" -->
<p class="dots">
  <!-- The <span> element corresponding to the current displayed slide
       will have the 'current' CSS class. -->
  <span [class]="selected.slide == 0 ? 'current' : ''" class="current"></span>
  <span [class]="selected.slide == 1 ? 'current' : ''"></span>
  <span [class]="selected.slide == 2 ? 'current' : ''"></span>
</p>

[class] — это привязка, которая изменяет атрибут class , и вы можете использовать ее для добавления или удаления классов CSS из любого элемента.

Теперь обновите страницу и попробуйте! Меняя слайд на карусели, он:

  1. Запускает событие slideChange ...
  2. Что вызывает действие AMP.setState ...
  3. Что обновляет переменную состояния selected.slide ...
  4. Что обновляет привязку [class] к элементам <span> индикатора!

Хороший! Теперь у нас есть рабочий слайд-индикатор.

Было бы неплохо, если бы мы могли видеть изображения разных цветов рубашки, когда меняем выбранный цвет. С помощью amp-bind мы можем сделать это, привязав [src] к элементам <amp-img> внутри <amp-carousel> .

Однако сначала нам нужно инициализировать данные состояния с URL-адресами источника изображения каждого цвета рубашки. Сделаем это с новым элементом <amp-state> :

<!-- Available shirts. Maps unique string identifier to color and image URL string. -->
<amp-state id="shirts">
  <script type="application/json">
    {
      "1001": {
        "color": "black",
        "image": "./shirts/black.jpg"
      },
      "1002": {
        "color": "blue",
        "image": "./shirts/blue.jpg"
      },
      "1010": {
        "color": "brown",
        "image": "./shirts/brown.jpg"
      },
      "1014": {
        "color": "dark green",
        "image": "./shirts/dark-green.jpg"
      },
      "1015": {
        "color": "gray",
        "image": "./shirts/gray.jpg"
      },
      "1016": {
        "color": "light gray",
        "image": "./shirts/light-gray.jpg"
      },
      "1021": {
        "color": "navy",
        "image": "./shirts/navy.jpg"
      },
      "1030": {
        "color": "wine",
        "image": "./shirts/wine.jpg"
      }
    }
  </script>
</amp-state>

Этот элемент <amp-state> содержит объект JSON, который сопоставляет строку идентификатора рубашки (т. е. SKU) с цветом и URL-адресом изображения соответствующей рубашки. Здесь также подойдет массив JSON, но использование объекта позволяет нам делать еще несколько интересных вещей, которые вы скоро увидите.

Теперь мы можем получить доступ к URL-адресу изображения через идентификатор рубашки. Например, shirts['10014'].color оценивается как "dark green" , а shirts['10030'].image возвращает URL-адрес изображения для рубашки "винного" цвета.

Если мы добавим еще одну переменную состояния, которая отслеживает выбранный SKU, мы можем привязать выражение к элементам <amp-img> для обновления их атрибутов src при изменении выбранного SKU. Добавьте новый ключ sku в JSON существующего элемента amp-state#selected :

<amp-state id="selected">
  <script type="application/json">
    {
      "slide": 0,
      "sku": "1001"
    }
  </script>
</amp-state>

Добавьте действие «on» в <amp-selector> , которое обновляет переменную selected.sku всякий раз, когда выбирается новый цвет:

<amp-selector name="color" 
    on="select:AMP.setState({selected: {sku: event.targetOption}})">

Затем добавьте привязки к элементам <amp-img> внутри <amp-carousel> (ищите <!-- TODO: "Changing images in amp-carousel-->" ):

<!-- Update the `src` of each <amp-img> when the `selected.sku` variable changes. -->
<amp-img width=200 height=250 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>
<amp-img width=300 height=375 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>
<amp-img width=400 height=500 src="./shirts/black.jpg"
    [src]="shirts[selected.sku].image"></amp-img>

Примечание. На практике каждое изображение в карусели, скорее всего, будет иметь разные src . Это можно сделать, заменив одно изображение массивом изображений. Для простоты в этой кодовой лаборатории используется одно изображение с разным увеличением.

Теперь обновите страницу и выберите другой цвет рубашки. Когда вы это сделаете, изображения карусели обновятся, чтобы показать рубашки выбранного цвета.

Что делать, если ваши связываемые данные слишком велики или сложны для извлечения при загрузке страницы? Или что, если у каждого SKU есть цена, поиск которой занимает много времени? Поиск цен на SKU для непросмотренных товаров — напрасная работа.

Получение доступных размеров для рубашки

Давайте воспользуемся возможностью получения удаленных данных, чтобы узнать цены на SKU в нашем примере кода. Наш сервер разработки Express.js в app.js уже имеет конечную точку /shirts/sizes?shirt=<sku> , которая, учитывая артикул рубашки, возвращает доступные размеры и цену для каждого размера. Он отправляет ответ с искусственной задержкой в ​​одну секунду, чтобы имитировать задержку в сети.

Запрос

Ответ

GET /shirts/sizesAndPrices?sku=1001

{"1001: {"sizes": {"XS": 8.99, "S" 9.99}}}

Подобно данным JSON в элементах <amp-state> , удаленные данные, возвращенные из этих выборок, объединяются и доступны в атрибуте id элемента. Например, данные, возвращенные из приведенного выше примера ответа, могут быть доступны в выражении:

Выражение

Результат

shirts['1001'].sizes['XS']

8.99

Теперь давайте применим это к нашему примеру электронной коммерции. Сначала давайте получим данные об этой рубашке, когда будет выбран новый SKU. Добавьте привязку [src] к нашему элементу amp-state#shirts :

<!-- When `selected.sku` changes, update the `src` attribute and fetch
     JSON at the new URL. Then, merge that data under `id` ("shirts"). -->
<amp-state id="shirts" [src]="'/shirts/sizesAndPrices?sku=' + selected.sku">

Далее давайте четко пометим недоступные размеры как таковые для данного артикула. "unavailable" класс CSS добавляет диагональную линию через элемент — мы можем добавить ее к элементам в пределах amp-selector[name="size"] в соответствии с недоступными размерами:

<amp-selector name="size">
  <table>
    <tr>
      <!-- If 'XS' size is available for selected SKU, return empty string.
           Otherwise, return 'unavailable'. -->
      <td [class]="shirts[selected.sku].sizes['XS'] ? '' : 'unavailable'">
        <div option="XS">XS</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['S'] ? '' : 'unavailable'">
        <div option="S">S</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['M'] ? '' : 'unavailable'">
        <div option="M">M</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['L'] ? '' : 'unavailable'">
        <div option="L">L</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['XL'] ? '' : 'unavailable'">
        <div option="XL">XL</div>
      </td>
    </tr>
  </table>
</amp-selector>

Теперь перезагрузите страницу и попробуйте. При выборе нового SKU (цвета рубашки) недоступные размеры будут перечеркнуты (после небольшой задержки).

Однако есть небольшая проблема — как насчет черной рубашки, цвета, выбранного по умолчанию? Нам нужно добавить данные о размере и цене черной рубашки в amp-state#shirts ...

<amp-state id="shirts" [src]="'/shirts/sizesAndPrices?sku=' + selected.sku">
  <script type="application/json">
    {
      "1001": {
        "color": "black",
        "image": "./shirts/black.jpg",
        "sizes": {
          "XS": 8.99,
          "S": 9.99
        }
      },
<!-- ... -->

...а также обновить состояние соответствующих элементов по умолчанию.

<amp-selector name="size">
  <table>
    <tr>
      <!-- If 'XS' size is available for selected SKU, return empty string.
           Otherwise, return 'unavailable'. -->
      <td [class]="shirts[selected.sku].sizes['XS'] ? '' : 'unavailable'">
        <div option="XS">XS</div>
      </td>
      <td [class]="shirts[selected.sku].sizes['S'] ? '' : 'unavailable'">
        <div option="S">S</div>
      </td>
      <!-- Add the ‘unavailable' class to the next three <td> elements
           to be consistent with the available sizes of the default SKU. -->
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['M'] ? '' : 'unavailable'">
        <div option="M">M</div>
      </td>
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['L'] ? '' : 'unavailable'">
        <div option="L">L</div>
      </td>
      <td class="unavailable" 
          [class]="shirts[selected.sku].sizes['XL'] ? '' : 'unavailable'">
        <div option="XL">XL</div>
      </td>
    </tr>
  </table>
</amp-selector>

Переменные цены на рубашки

Теперь, когда мы правильно отображаем доступные размеры, давайте удостоверимся, что отображается и правильная цена.

Наш магазин AMPPAREL уникален тем, что цена рубашки зависит как от цвета, так и от размера. Это означает, что нам нужна новая переменная для отслеживания выбранного пользователем размера. Добавьте новое действие к элементу size <amp-selector> :

<!-- When an element is selected, set the `selectedSize` variable to the
     value of the "option" attribute of the selected element.  -->
<amp-selector name="size" 
    on="select:AMP.setState({selectedSize: event.targetOption})">

Обратите внимание, что мы не инициализируем значение selectedSize через элемент amp-state#selected . Это потому, что мы намеренно не предоставляем выбранный размер по умолчанию и вместо этого хотим заставить пользователя выбрать размер.

Добавьте новый элемент <span> , обертывающий ценник, и измените текст по умолчанию на «---», так как нет выбора размера по умолчанию.

<h6>PRICE :
  <!-- Display the price of the selected shirt in the selected size if available.
       Otherwise, display the placeholder text '---'. -->
  <span [text]="shirts[selected.sku].sizes[selectedSize] || '---'">---</span>
</h6>

И у нас правильные цены! Попробуйте.

Условно включенная кнопка

Мы почти закончили! Теперь давайте отключим кнопку «Добавить в корзину», когда выбранный размер недоступен:

<!-- Disable the "ADD TO CART" button when:
     1. There is no selected size, OR
     2. The available sizes for the selected SKU haven't been fetched yet
-->
<input type="submit" value="ADD TO CART" disabled
    class="mdl-button mdl-button--raised mdl-button--accent"
    [disabled]="!selectedSize || !shirts[selected.sku].sizes[selectedSize]">

У нас есть интерактивная страница сведений о продукте электронной коммерции с переменными размерами и ценами для каждого SKU, получаемая по запросу с удаленной конечной точки JSON.

Если вы застряли, см. static/final.html для полного решения.

Мы надеемся, что эта лаборатория покажет вам мощь и гибкость создания интерактивных страниц AMP с помощью <amp-bind> . Для получения дополнительной информации ознакомьтесь с документацией <amp-bind> .

Мы любим получать обратную связь — пожалуйста, присылайте нам свои пожелания, предложения или отчеты об ошибках. Если вы хотите протестировать <amp-bind> с реальными пользователями, подумайте о том, чтобы подать заявку на пробную версию Origin .

Мы рады вскоре запустить <amp-bind> и посмотреть, что вы можете с его помощью создать!