Favorite Button
Introduction
This sample demonstrates how to implement a favorite/like/bookmark button in AMP. Our implementation:
- shows the correct icon based on whether the user already liked an item or not. This works if the AMP is served from an AMP Cache or the original origin.
- shows a placeholder while the current state is loaded asynchronously.
- falls back to the original state and displays an error message if the request fails, for example when the user is offline.
Setup
We use the amp-list
component to dynamically render the initial state of the favorite button.
<script async custom-element="amp-list" src="https://cdn.ampproject.org/v0/amp-list-0.1.js"></script>
The mustache component is required by amp-list
.
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.2.js"></script>
We need amp-form
to submit the favorite request.
<script async custom-element="amp-form" src="https://cdn.ampproject.org/v0/amp-form-0.1.js"></script>
amp-bind
enables us to dynamically change the button state when we submit the form.
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
Managing state
We initialize the button state from a JSON endpoint using amp-state.
As we use cookies to identify the user, we need to add the credentials="include"
attribute.
<amp-state id="favorite" credentials="include" src="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite">
</amp-state>
A simple favorite button
The button is embedded inside an amp-list
, which enables us to dynamically render the button based on whether
the user already liked something or not. Inside the template we use mustache's implicit iterator .
to access the boolean
value that is returned by our /favorite
endpoint: {{#.}}heart-fill{{/.}}
.
We also declare a placeholder icon inside the amp-list
using the placeholder
attribute which
is shown while the amp-list
is loading.
There are a few things that are happening when the user presses the favorite button:
- The form sends a toggle request when the user presses the button.
- We implement an optimistic UX that will instantly update the button state when the button is pressed.
- If the form submission fails (
submit-error:...
), we revert the favorite state to the original version and show an error message (favorite-failed-message.show
). - We hide any existing error messages (
favorite-failed-message.hide
).
<form class="favorite-button" method="post" action-xhr="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite" target="_top" on="submit:AMP.setState({
favorite: !favorite
}),
favorite-failed-message.hide;
submit-error:AMP.setState({
favorite: !favorite
}),
favorite-failed-message.show">
<amp-list width="56" height="56" credentials="include" items="." single-item src="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite" binding="always">
<template type="amp-mustache">
<input type="submit" class="{{#.}}heart-fill{{/.}}{{^.}}heart-border{{/.}}" [class]="favorite ? 'heart-fill' : 'heart-border'" value aria-label="Favorite Toggle">
</template>
<div placeholder>
<input type="submit" disabled class="heart-loading" value aria-label="favorite placeholder">
</div>
</amp-list>
</form>
A simple snackbar that we show when the form submission fails.
<div id="favorite-failed-message" hidden>Error: Could not favorite.
<div on="tap:favorite-failed-message.hide" tabindex="0" role="button">CLOSE</div>
</div>
A favorite button with counter
This is a more sophisticated version of the previous sample that also includes the number of favorites. Our JSON endpoint returns two values: value
and count
.
<amp-state id="favoriteWithCount" credentials="include" src="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite-with-count">
</amp-state>
The implementation is similar to the previous sample, but also updates the count when the button is clicked
AMP.setState({ ..., count: favoriteWithCount.count + (favoriteWithCount.value ? -1 : 1) })
We use a temporary variable previousFavoriteWithCount
to store the previous value in order to be able to revert the
button state in case the form submission fails.
<form class="favorite-button" method="post" action-xhr="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite-with-count" target="_top" on="submit:AMP.setState({
previousFavoriteWithCount: favoriteWithCount,
favoriteWithCount: {
value: !favoriteWithCount.value,
count: favoriteWithCount.count + (favoriteWithCount.value ? -1 : 1),
}
}),
favorite-failed-message.hide;
submit-error:AMP.setState({
value: !favoriteWithCount.value,
favoriteWithCount: previousFavoriteWithCount.count
}),
favorite-failed-message.show">
<amp-list width="200" height="56" credentials="include" items="." single-item noloading src="https://amp.dev/documentation/examples/interactivity-dynamic-content/favorite_button/favorite-with-count" binding="always">
<template type="amp-mustache">
<div class="favorite-container">
<input type="submit" class="{{#value}}heart-fill{{/value}}{{^value}}heart-border{{/value}}" [class]="favoriteWithCount.value ? 'heart-fill' : 'heart-border'" value aria-label="Favorite Toggle">
<div class="favorite-count" [text]="favoriteWithCount.count ? favoriteWithCount.count : ''">{{count}}</div>
</div>
</template>
<div placeholder>
<div class="favorite-container">
<input type="submit" disabled class="heart-loading" value aria-label="favorite placeholder">
<div class="favorite-count loading">0</div>
</div>
</div>
</amp-list>
</form>
Sollten die Erklärungen auf dieser Seite nicht all deine Fragen beantworten, kannst du dich gerne an andere AMP Nutzer wenden, um deinen konkreten Use Case zu besprechen.
Zu Stack Overflow wechseln Ein Feature wurde nicht erklärt?Das AMP Projekt ist auf deine Teilnahme und deine Beiträge angewiesen! Wir hoffen natürlich, dass du dich aktiv an unserer Open Source Community beteiligen wirst. Aber wir freuen uns auch über einmalige Beiträge zu den Themen, die dich besonders interessieren.
Beispiel auf GitHub bearbeiten-
Written by @sebastianbenz