AMP

amp-bind

Ce composant ajoute une interactivité personnalisée en utilisant la liaison de données et des expressions.

Script requis
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
Exemples
DidacticielsCréer des pages AMP interactives

Aperçu

Le composant amp-bind vous permet d'ajouter une interactivité avec état personnalisée à vos pages AMP au moyen de la liaison de données et d'expressions de type JS.

Regardez cette vidéo pour découvrir le composant amp-bind.

Exemple simple

Dans l'exemple suivant, le fait d'appuyer sur le bouton remplace le texte "Hello World" de l'élément <p> par "Hello amp-bind".

<p [text]="'Hello ' + foo">Hello World</p>

<button on="tap:AMP.setState({foo: 'amp-bind'})">Say "Hello amp-bind"</button>

Pour des raisons de performances et pour éviter un saut de contenu inattendu, amp-bind n'évalue pas les expressions lors du chargement de la page. Cela signifie que les éléments visuels doivent être associés à un état par défaut et qu'il ne faut pas utiliser amp-bind pour l'affichage initial.

Comment cela fonctionne-t-il ?

amp-bind comprend trois composants principaux :

  1. State : état JSON mutable à l'échelle du document. Dans l'exemple ci-dessus, l'état est vide avant que l'utilisateur appuie sur le bouton. Une fois le bouton enfoncé, l'état devient {foo: 'amp-bind'}.
  2. Expressions : expressions de type JavaScript pouvant faire référence à l'état. L'exemple ci-dessus contient une seule expression, 'Hello ' + foo, qui concatène le littéral de chaîne 'Hello ' et la variable d'état foo. Le nombre d'opérandes pouvant être utilisés dans une expression est limité à 100.
  3. Bindings : attributs spéciaux sous la forme [property] qui associent la propriété d'un élément à une expression. L'exemple ci-dessus comporte une seule liaison, [text], qui met à jour le texte de l'élément <p> chaque fois que la valeur de l'expression change.

amp-bind veille tout particulièrement à garantir la vitesse, la sécurité et les performances sur les pages AMP.

Voici un exemple un peu plus complexe

<!-- Store complex nested JSON data in <amp-state> elements. -->
<amp-state id="myAnimals">
  <script type="application/json">
    {
      "dog": {
        "imageUrl": "/img/dog.jpg",
        "style": "greenBackground"
      },
      "cat": {
        "imageUrl": "/img/cat.jpg",
        "style": "redBackground"
      }
    }
  </script>
</amp-state>

<p [text]="'This is a ' + currentAnimal + '.'">Ceci est un chien.</p>

<!-- CSS classes can also be added or removed with [class]. -->
<p class="greenBackground" [class]="myAnimals[currentAnimal].style">
  Chaque animal a une couleur de fond différente.
</p>

<!-- Or change an image's src with the [src] binding. -->
<amp-img width="300" height="200" src="/img/dog.jpg" [src]="myAnimals[currentAnimal].imageUrl">
</amp-img>

<button on="tap:AMP.setState({currentAnimal: 'cat'})">Set to Cat</button>

Lorsque l'on appuie sur le bouton :

  1. L'état est mis à jour avec l'attribut currentAnimal défini sur 'cat'.
  2. Les expressions qui dépendent de currentAnimal sont évaluées :

    • 'This is a ' + currentAnimal + '.' => 'This is a cat.'
    • myAnimals[currentAnimal].style => 'redBackground'
    • myAnimals[currentAnimal].imageUrl => /img/cat.jpg
  3. Les liaisons qui dépendent des expressions modifiées sont mises à jour :

    • Le texte du premier élément <p> sera "This is a cat."
    • L'attribut class du deuxième élément <p> sera "redBackground".
    • L'élément amp-img affichera l'image d'un chat.

Regardez la démo de cet exemple avec des annotations de code.

Détails

État

Chaque document AMP qui utilise le composant amp-bind comprend des données JSON mutables à l'échelle du document, désignées sous le nom d'état.

Initialisation de l'état avec amp-state

L'état d'amp-bind peut être initialisé avec le composant amp-state :

<amp-state id="myState">
  <script type="application/json">
    {
      "foo": "bar"
    }
  </script>
</amp-state>

Les expressions peuvent faire référence à des variables d'état via la syntaxe à points. Dans cet exemple, myState.foo est évalué sur "bar".

  • La taille maximale du fichier JSON enfant d'un élément <amp-state> est de 100 Ko.
  • Un élément <amp-state> peut également spécifier une URL CORS au lieu d'un script JSON enfant. Pour plus d'informations, reportez-vous à l'Annexe.

Actualisation de l'état

L'action refresh est compatible avec ce composant. Elle peut être utilisée pour actualiser le contenu de l'état.

<amp-state id="amp-state" ...></amp-state>
<!-- Clicking the button will refresh and refetch the json in amp-state. -->
<button on="tap:amp-state.refresh"></button>

Mise à jour de l'état avec AMP.setState()

L'action AMP.setState() fusionne un littéral d'objet dans l'état. Par exemple, lorsque l'on appuie sur le bouton ci-dessous, AMP.setState() effectue une fusion profonde du littéral d'objet avec l'état.

<!-- Like JavaScript, you can reference existing
    variables in the values of the  object literal. -->
<button on="tap:AMP.setState({foo: 'bar', baz: myAmpState.someVariable})"></button>

En règle générale, la profondeur de fusion maximale des objets imbriqués est de 10 niveaux. Toutes les variables, y compris celles introduites par amp-state, peuvent être remplacées.

Lorsqu'elle est déclenchée par certains événements, l'action AMP.setState() peut également accéder aux données relatives aux événements sur la propriété event.

<!-- The "change" event of this <input> element contains
      a "value" variable that can be referenced via "event.value". -->
<input type="range" on="change:AMP.setState({myRangeValue: event.value})">

Modification de l'historique avec AMP.pushState()

L'action AMP.pushState() est semblable à AMP.setState(), si ce n'est qu'elle envoie une nouvelle entrée dans la pile d'historique du navigateur. Faire apparaître cette entrée d'historique (en revenant en arrière, par exemple) a pour effet de rétablir la valeur précédente des variables définie par AMP.pushState().

Exemple :

<button on="tap:AMP.pushState({foo: '123'})">Set 'foo' to 123</button>
  • En appuyant sur le bouton, vous définissez la variable foo sur 123 et envoyez une nouvelle entrée d'historique.
  • En revenant en arrière, vous redéfinissez la variable foo sur sa valeur antérieure, à savoir "bar" (ce qui revient à appeler AMP.setState({foo: 'bar'}).

Expressions

Les expressions sont semblables à JavaScript, avec toutefois quelques différences importantes.

Différences par rapport à JavaScript

  • Les expressions peuvent uniquement accéder à l'état du document conteneur.
  • Les expressions n'ont pas accès à des données globales telles que window ou document.
  • Seuls les opérateurs et les fonctions sur liste blanche peuvent être utilisés.
  • En règle générale, les fonctions, classes et boucles personnalisées ne sont pas autorisées. Les fonctions fléchées sont autorisées en tant que paramètres ; Array.prototype.map, par exemple.
  • Les variables non définies et les exceptions array-index-out-of-bounds renvoient la valeur null au lieu de renvoyer undefined ou de générer des erreurs.
  • Une expression unique est actuellement limitée à 50 opérandes afin d'optimiser les performances. N'hésitez pas à nous contacter si cela s'avère insuffisant dans votre cas.

Pour consulter toute la mise en œuvre et la grammaire complète de l'expression, reportez-vous aux pages bind-expr-impl.jison et bind-expression.js.

Exemples

Toutes les expressions suivantes sont valides :

1 + '1'           // 11
1 + (+'1')        // 2
!0                // true
null || 'default' // 'default'

Fonctions sur liste blanche

Type d'objetFonction(s)Exemple
Tableau1concat
filter
includes
indexOf
join
lastIndexOf
map
reduce
slice
some
sort (not-in-place)
splice (not-in-place)
// Returns [1, 2, 3].
          [3, 2, 1].sort()
// Returns [1, 3, 5].
            [1, 2, 3].map((x, i) => x + i)
// Returns 6.
              [1, 2, 3].reduce((x, y) => x + y)
NombretoExponential
toFixed
toPrecision
toString
// Returns 3.
                (3.14).toFixed()
// Returns '3.14'.
                  (3.14).toString()
ChaînecharAt
charCodeAt
concat
indexOf
lastIndexOf
slice
split
substr
substring
toLowerCase
toUpperCase
// Returns 'abcdef'.
                      abc'.concat('def')
Fonctions mathématiques2abs
ceil
floor
max
min
random
round
sign
// Returns 1.
                          abs(-1)
Objet2keys
values
// Returns ['a', 'b'].
                            keys({a: 1, b: 2})
// Returns [1, 2].
                              values({a: 1, b: 2}
Global2encodeURI
encodeURIComponent
// Returns 'Hello%20world'.
                                encodeURIComponent('Hello world')

1 Les fonctions fléchées à un seul paramètre ne peuvent pas contenir de parenthèses. Utilisez, par exemple, x => x + 1 au lieu de (x) => x + 1. En outre, sort() et splice() renvoient des copies modifiées au lieu de s'exécuter sur place. 2 Les fonctions statiques sont dépourvues d'espace de noms ; utilisez, par exemple, abs(-1) au lieu de Math.abs(-1).

Définir des macros avec amp-bind-macro

Les fragments d'expression amp-bind peuvent être réutilisés en définissant un élément amp-bind-macro. L'élément amp-bind-macro vous permet de définir une expression qui utilise zéro ou plusieurs arguments et fait référence à l'état actuel. Une macro peut être appelée comme une fonction en référençant sa valeur d'attribut id depuis n'importe quel point du document.

<amp-bind-macro id="circleArea" arguments="radius" expression="3.14 * radius * radius"></amp-bind-macro>

<div>
  L'aire du cercle est de <span [text]="circleArea(myCircle.radius)">0</span>.
</div>

Une macro peut également en appeler d'autres qui sont définies avant elle. En revanche, elle ne peut pas s'appeler de manière récursive.

Liaisons

Une liaison est un attribut spécial sous la forme [property] qui associe la propriété d'un élément à une expression. Une autre syntaxe compatible avec XML peut également être utilisée sous la forme data-amp-bind-property.

Lorsque l'état change, les expressions sont réévaluées et les propriétés des éléments liés sont mises à jour avec les nouveaux résultats d'expression.

amp-bind accepte les liaisons de données sur quatre types d'état d'élément :

TypeAttribut(s)Détails
Node.textContent[text]Compatible avec la plupart des éléments textuels.
Classes CSS[class]Le résultat de l'expression doit être une chaîne délimitée par des espaces.
Attribut hidden[hidden]Il doit s'agir d'une expression booléenne.
Taille des éléments AMP[width]
[height]
Modifie la largeur et/ou la hauteur de l'élément AMP.
Attributs spécifiques aux élémentsDivers

Remarques sur les liaisons :

  • Pour des raisons de sécurité, la liaison vers innerHTML n'est pas autorisée.
  • Les valeurs qui présentent un risque sont effacées de toutes les liaisons d'attribut (javascript:, par exemple).
  • Les résultats des expressions booléennes font varier les attributs booléens. Prenons l'exemple de <amp-video [controls]="expr"...>. Lorsque expr est défini sur true, l'attribut controls est associé à l'élément <amp-video>. Lorsque expr est défini sur false, l'attribut controls est supprimé.
  • L'utilisation de crochets ([ et ]) dans les noms d'attribut peut poser problème lors de la rédaction de code XML (XHTML, JSX, etc.) ou de l'écriture d'attributs au moyen d'API DOM. Dans ce cas, utilisez la syntaxe data-amp-bind-x="foo" au lieu de [x]="foo".

Attributs spécifiques aux éléments

Seule la liaison aux composants et attributs suivants est autorisée :

ÉlémentAttribut(s)Comportement
<amp-brightcove>[data-account]
[data-embed]
[data-player]
[data-player-id]
[data-playlist-id]
[data-video-id]
Modifie la vidéo Brightcove affichée.
<amp-carousel type=slides>[slide]*Modifie l'index des diapositives en cours d'affichage. Voir un exemple.
<amp-date-picker>[min]
[max]
Définit la date la plus ancienne pouvant être sélectionnée.
Définit la date la plus proche pouvant être sélectionnée.
<amp-google-document-embed>[src]
[title]
Affiche le document au niveau de l'URL mise à jour.
Modifie le titre du document.
<amp-iframe>[src]Modifie l'URL source de l'iFrame.
<amp-img>[alt]
[attribution]
[src]
[srcset]
En cas de liaison à [src], veillez également à lier [srcset] pour que la liaison fonctionne sur le cache.
Voir les attributs amp-img correspondants.
<amp-lightbox>[open]*Active/désactive l'affichage du mode Lightbox. Conseil : Utilisez on="lightboxClose: AMP.setState(...)" pour mettre à jour les variables lorsque le mode Lightbox est désactivé.
<amp-list>[src]Si l'expression est une chaîne, cet élément récupère et affiche le code JSON depuis l'URL de la chaîne. Si l'expression est un objet ou un tableau, cet élément affiche les données d'expression.
<amp-selector>[selected]*
[disabled]
Modifie le ou les éléments enfants sélectionnés
qui sont identifiés par leurs valeurs d'attribut option. Une liste de valeurs séparées par des virgules est acceptée pour sélectionner plusieurs éléments. Voir un exemple.
<amp-state>[src]Récupère le code JSON de la nouvelle URL et le fusionne dans l'état existant. Notez que la mise à jour suivante ignorera les éléments <amp-state> afin d'éviter les cycles.
<amp-video>[alt]
[attribution]
[controls]
[loop]
[poster]
[preload]
[src]
Voir les attributs amp-video correspondants.
<amp-youtube>[data-videoid]Change la vidéo YouTube affichée.
<a>[href]Modifie le lien.
<button>[disabled]
[type]
[value]
Voir les attributs button correspondants.
<details>[open]Voir les attributs details correspondants.
<fieldset>[disabled]Active ou désactive le jeu de champs.
<image>[xlink:href]
Voir les attributs image correspondants.
<input>[accept]
[accessKey]
[autocomplete]
[checked]
[disabled]
[height]
[inputmode]
[max]
[maxlength]
[min]
[minlength]
[multiple]
[pattern]
[placeholder]
[readonly]
[required]
[selectiondirection]
[size]
[spellcheck]
[step]
[type]
[value]
[width]
Voir les attributs input correspondants.
<option>[disabled]
[label]
[selected]
[value]
Voir les attributs option correspondants.
<optgroup>[disabled]
[label]
Voir les attributs optgroup correspondants.
<select>[autofocus]
[disabled]
[multiple]
[required]
[size]
Voir les attributs select correspondants.
<source>[src]
[type]
Voir les attributs source correspondants.
<track>[label]
[src]
[srclang]
Voir les attributs track correspondants.
<textarea>[autocomplete]
[autofocus]
[cols]
[disabled]
[maxlength]
[minlength]
[placeholder]
[readonly]
[required]
[rows]
[selectiondirection]
[selectionend]
[selectionstart]
[spellcheck]
[wrap]
Voir les attributs textarea correspondants.

* Désigne les attributs pouvant être liés auxquels ne correspond aucun élément qui ne peut pas l'être.

Débogage

Effectuez un test en mode de développement (avec le fragment d'URL #development=1) pour mettre en évidence les avertissements et les erreurs générés pendant le développement, et pour accéder à des fonctions de débogage spéciales.

Avertissements

En mode de développement, amp-bind émet un avertissement lorsque la valeur par défaut d'un attribut lié ne correspond pas au résultat initial de l'expression équivalente. Cela permet d'éviter les mutations indésirables consécutives aux modifications apportées à d'autres variables d'état. Par exemple :

<!-- The element's default class value ('def') doesn't match the    
     expression result for [class] ('abc'),
     so a warning will be issued in development mode. -->

<p class="def" [class]="'abc'"></p>

En mode de développement, amp-bind émet également un avertissement lors du déréférencement de variables ou de propriétés non définies. Cela permet, en outre, d'éviter les mutations indésirables dues aux résultats d'expression null. Par exemple :

<amp-state id="myAmpState">
  <script type="application/json">
    { "foo": 123 }
</script>
</amp-state></p>

<!-- The amp-state#myAmpState does not have a `bar` variable, so a warning
  will be issued in development mode. -->

<p [text]="myAmpState.bar">Texte d'espace réservé.</p>

Erreurs

Plusieurs types d'erreurs d'exécution peuvent se produire lors de l'utilisation du composant amp-bind.

TypeMessageSuggestion
Liaison incorrecteBinding to [someBogusAttribute] on <P> is not allowed.Utilisez uniquement des liaisons sur liste blanche.
Erreur de syntaxeExpression compilation error in...Vérifiez que l'expression ne contient pas de fautes de frappe.
Fonctions ne figurant pas sur liste blanchealert is not a supported function.Utilisez uniquement des fonctions sur liste blanche.
Résultat expurgé"javascript:alert(1)" is not a valid result for [href].Évitez les expressions ou protocoles d'URL interdits qui entraîneraient l'échec de validateur AMP.
Non-respect de la stratégie de sécurité du contenu (CSP)Refused to create a worker from 'blob:...' because it violates the following Content Security Policy directive...Ajoutez default-src blob: à la stratégie de sécurité du contenu (CSP) de votre origine. amp-bind délègue les tâches fastidieuses à un web worker dédié pour garantir un niveau de performances élevé.

État de débogage

Utilisez AMP.printState() pour imprimer l'état actuel de la console.

Annexe

Spécification de l'élément <amp-state>

Un élément amp-state peut être composé d'un élément <script> enfant OU d'un attribut src contenant une URL CORS vers un point de terminaison JSON distant, mais pas des deux.

<amp-state id="myLocalState">
  <script type="application/json">
    {
      "foo": "bar"
      }
  </script>
</amp-state></p>

<p><amp-state id="myRemoteState" src="https://data.com/articles.json">
</amp-state>

Traitements par lots de requêtes XHR

AMP regroupe les requêtes XHR (XMLHttpRequest) dans des points de terminaison JSON. En d'autres termes, vous pouvez utiliser une seule requête de données JSON comme source de données pour plusieurs consommateurs (plusieurs éléments amp-state, par exemple) sur une page AMP. Supposons que l'élément amp-state adresse une requête XHR à un point de terminaison. Dans ce cas, lorsque la requête XHR sera en cours, les requêtes XHR ultérieures adressées au même point de terminaison ne se déclencheront pas et renverront, à la place, les résultats à partir de la première requête XHR.

Attributs

srcURL du point de terminaison distant qui renvoie le fichier JSON qui mettra à jour cet élément amp-state. Il doit s'agir d'un service HTTP CORS. L'attribut src autorise toutes les substitutions de variables d'URL standards. Pour plus d'informations, consultez le Guide des substitutions.

Le point de terminaison doit mettre en œuvre les exigences énoncées dans la spécification Requêtes CORS dans AMP.

credentials (facultatif)Définit une option credentials telle qu'elle est spécifiée par l'API Fetch.
  • Valeurs acceptées : `omit`, `include`
  • Valeur par défaut : `omit`
Pour envoyer des identifiants, transmettez la valeur include. Si cette valeur est définie, la réponse doit respecter les consignes de sécurité CORS dans AMP.

Fusion en profondeur avec AMP.setState()

Lorsque l'action AMP.setState() est appelée, amp-bind fusionne en profondeur le littéral d'objet fourni avec l'état actuel. Toutes les variables du littéral d'objet sont écrites directement dans l'état, à l'exception des objets imbriqués, qui sont fusionnés de manière récursive. Les primitives et les tableaux qui se trouvent dans l'état sont toujours écrasés par les variables portant le même nom dans le littéral d'objet.

Prenons l'exemple suivant :

{
  <!-- State is empty -->
  }
<button on="tap:AMP.setState({employee: {name: 'John Smith', age: 47, vehicle: 'Car'}})"...></button>
<button on="tap:AMP.setState({employee: {age: 64}})"...></button>

Lorsque vous appuyez sur le premier bouton, l'état devient :

{
  employee: {
    name: 'John Smith',
    age: 47,
    vehicle: 'Car',
    }
  }

Lorsque vous appuyez sur le deuxième bouton, amp-bind fusionne de manière récursive l'argument de littéral de l'objet, {employee: {age: 64}}, dans l'état existant.

{
  employee: {
    name: 'John Smith',
    age: 64,
    vehicle: 'Car',
    }
  }

employee.age a été mis à jour, mais les clés employee.name et employee.vehicle n'ont pas changé.

Notez que le composant amp-bind génère une erreur si vous appelez AMP.setState() avec un littéral d'objet contenant des références circulaires.

Supprimer une variable

Pour supprimer une variable d'état, définissez sa valeur sur null dans AMP.setState(). En commençant par l'état de l'exemple précédent, si vous appuyez sur :

<button on="tap:AMP.setState({employee: {vehicle: null}})"...></button>

L'état est remplacé par :

{
  employee: {
    name: 'John Smith',
    age: 48,
    }
  }

De même, si vous appuyez sur :

<button on="tap:AMP.setState({employee: null})"...></button>

L'état est remplacé par :

{
  <!-- State is empty -->
  }

Grammaire des expressions

Grammaire de type BNF pour les expressions amp-bind :

expr:
    operation
  | invocation
  | member_access
  | '(' expr ')'
  | variable
  | literal

operation:
    !' expr
    | '-' expr
    | '+' expr
    | expr '+' expr
    | expr '-' expr
    | expr '*' expr
    | expr '/' expr
    | expr '%' expr
    | expr '&&' expr
    | expr '||' expr
    | expr '<=' expr
    | expr '<' expr
    | expr '>=' expr
    | expr '>;' expr
    | expr '!=' expr
    | expr '==' expr
    | expr '?' expr ':' expr

  invocation:
      expr '.' NAME args

    args:
        (' ')'
        | '(' array ')'
        ;

      member_access:
          expr member
          ;

        member:
            .' NAME
            | '[' expr ']'

          variable:
              NAME
              ;

            literal:
                STRING
                | NUMBER
                | TRUE
                | FALSE
                | NULL
                | object_literal
                | array_literal

              array_literal:
                  [' ']'
                  | '[' array ']'

                array:
                    expr
                    | array ',' expr

                  object_literal:
                      {' '}'
                      | '{' object '}'

                    object:
                        key_value
                        | object ',' key_value

                      key_value:
                          expr ':' expr
Need more help?

You've read this document a dozen times but it doesn't really cover all of your questions? Maybe other people felt the same: reach out to them on Stack Overflow.

Go to Stack Overflow
Found a bug or missing a 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.

Go to GitHub