amp-bind
Description
データ バインディングや JS に似た単純な式を使用して、ユーザーの操作やデータの変更に応じた要素の変更を可能にします。
Required Scripts
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>
実例
データ バインディングと式を使用して独自のインタラクティブ性を追加します。
必要なスクリプト | <script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script> |
例 | |
チュートリアル | インタラクティブな AMP ページを作成する |
概要
amp-bind
コンポーネントを使用すると、データ バインディングと JS に似た式を使って独自のステートフルなインタラクティブ性を AMP ページに追加できます。
簡単な例
次の例では、ボタンをタップすると、<p>
要素のテキストが「Hello World」から「Hello amp-bind」に変わります。
<p [text]="'Hello ' + foo">Hello World</p>
<button on="tap:AMP.setState({foo: 'amp-bind'})">Say "Hello amp-bind"</button>
amp-bind
では、パフォーマンスの向上と、予期しないコンテンツの移動が発生するリスクの回避のために、ページの読み込み時に式の評価を行いません。そのため、表示要素のデフォルトの状態を指定して、最初のレンダリングでamp-bind
を使用しないようにしてください。 仕組み
amp-bind
には以下の 3 つの主要なコンポーネントがあります。
- 状態 : ドキュメント スコープの変更可能な JSON ステータス。上の例では、ボタンをタップする前の状態は空です。ボタンをタップした後の状態は
{foo: 'amp-bind'}
です。 - 式 : 状態を参照可能な、JavaScript に似た式です。上の例の式
'Hello ' + foo
は、文字列リテラル'Hello'
とステータス変数foo
を連結します。式で使用できるオペランドには 100 個の制限があります。 - バインディング: : 要素のプロパティを式にリンクする、[プロパティ] の形式の特別な属性です。上の例のバインディング [
text
] は、式の値が変化するたびに要素のテキストを更新します。
amp-bind
`では、AMP ページの速度、セキュリティ、パフォーマンスを確保するために特別な措置が取られています。
少し複雑な例
<!-- 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 + '.'">This is a dog.</p>
<!-- CSS classes can also be added or removed with [class]. -->
<p class="greenBackground" [class]="myAnimals[currentAnimal].style">
Each animal has a different background color.
</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>
<p><button on="tap:AMP.setState({currentAnimal: 'cat'})">Set to Cat</button>
ボタンを押すと、次のようになります。
- 状態が更新されます(
currentAnimal
がd'cat'
に定義されます)。 -
currentAnimal
に依存する式が評価されます。'This is a ' + currentAnimal + '.'
=>'This is a cat.'
myAnimals[currentAnimal].style
=>'redBackground'
myAnimals[currentAnimal].imageUrl
=>/img/cat.jpg
-
変更された式に依存するバインディングが更新されます。
- 1 つ目の
<p>
要素のテキストが「This is a cat.」になります。 - 2 つ目の
<p>
要素のclass
属性が「redBackground」になります。 amp-img
要素によって猫の画像が表示されます。
- 1 つ目の
詳細
状態
amp-bind
を使用する各 AMP ドキュメントでは、ドキュメント スコープの変更可能な JSON データ(状態)が作成されます。
amp-state
による状態の初期化
amp-bind
の状態は、amp-state
コンポーネントを通じて初期化できます。
<amp-state id="myState">
<script type="application/json">
{
"foo": "bar"
}
</script>
</amp-state>
式では、ドット構文によってステータス変数を参照できます。この例の myState.foo
は "bar"
として評価されます。
<amp-state>
要素の子 JSON の最大サイズは 100 KB です。<amp-state>
要素では、子 JSON スクリプトの代わりに CORS URL を指定することもできます。詳しくは、付録をご覧ください。
状態の更新
状態コンポーネントでは refresh
アクションがサポートされており、このアクションを使用して状態の内容を更新することができます。
<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>
AMP.setState()
による状態の更新
AMP.setState()
アクションは、オブジェクト リテラルを状態にマージします。たとえば、下のボタンを押すと、AMP.setState()
によってオブジェクト リテラルが状態にディープマージされます。
<!-- 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>
一般に、ネストされたオブジェクトをマージする場合の最大深度は 10 です。変数(amp-state
によって導入された変数を含む)はすべてオーバーライド可能です。
AMP.setState()
は、特定のイベントによってトリガーされた場合、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})">
AMP.pushState()
による履歴の変更
AMP.pushState()
アクションは、新しいエントリもブラウザの履歴スタックにプッシュすることを除き、AMP.setState()
に似ています。この履歴エントリを(戻るなどの操作によって)ポップすると、AMP.pushState()
で設定された以前の変数の値が復元されます。
例:
<button on="tap:AMP.pushState({foo: '123'})">Set 'foo' to 123</button>
- ボタンをタップすると、変数
foo
が 123 に設定され、新しい履歴エントリがプッシュされます。 - 戻る操作を行うと、
foo
が以前の値 "bar" に復元されます(AMP.setState({foo: 'bar'})
を呼び出すのと同じ効果があります)。
式
式は JavaScript に似ていますが、重要な違いがいくつかあります。
JavaScript との違い
- 式は、含んでいるドキュメントの状態にのみアクセスできます。
- 式は、
window
やdocument
などのグローバル変数にはアクセスできません。 - ホワイトリストに登録されている関数と演算子のみを使用できます。
- 一般に、独自の関数、クラス、ループは使用できません。arrow 関数はパラメータとして使用できます(例:
Array.prototype.map
)。 - 未定義の変数と array-index-out-of-bounds は、
undefined
を返したりエラーをスローしたりするのではなく、null
を返します。 - パフォーマンスの向上のために、現在は 1 つの式で使用できるオペランドの数が 50 個に制限されています。不十分な場合はお問い合わせください。
式の文法と実装について詳しくは、bind-expr-impl.jison と bind-expression.js をご覧ください。
例
以下はすべて有効な式です。
1 + '1' // 11
1 + (+'1') // 2
!0 // true
null || 'default' // 'default'
ホワイトリストに登録されている関数
オブジェクトの種類 | 関数 | 例 |
---|---|---|
配列 1 | concat 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) |
数値 | toExponential toFixed toPrecision toString | // Returns 3. (3.14).toFixed() // Returns '3.14'. (3.14).toString() |
文字列 | charAt charCodeAt concat indexOf lastIndexOf slice split substr substring toLowerCase toUpperCase | // Returns 'abcdef'. abc'.concat('def') |
計算 2 | abs ceil floor max min random round sign | // Returns 1. abs(-1) |
オブジェクト 2 | keys values | // Returns ['a', 'b']. keys({a: 1, b: 2}) // Returns [1, 2]. values({a: 1, b: 2} |
グローバル 2 | encodeURI encodeURIComponent | // Returns 'Hello%20world'. encodeURIComponent('Hello world') |
1 パラメータが 1 つだけの arrow 関数では、かっこは使用できません(例: (x) => x + 1
ではなく、x => x + 1
を使用します)。また、sort()
と splice()
は、in-place を操作するのではなく、変更されたコピーを返します。
2 静的関数は名前空間化されません(例: Math.abs(-1)
ではなく、abs(-1)
を使用します)。
amp-bind-macro
によるマクロの定義
amp-bind-macro
を定義することにより、amp-bind
の式フラグメントを再利用できます。amp-bind-macro
要素を使用すると、0 個以上の引数を取り、現在の状態を参照する式を定義できます。マクロは、ドキュメント内の任意の場所から id
属性の値を参照することで、関数のように呼び出すことができます。
<amp-bind-macro id="circleArea" arguments="radius" expression="3.14 * radius * radius"></amp-bind-macro>
<div>
The circle has an area of <span [text]="circleArea(myCircle.radius)">0</span>.
</div>
マクロは、自身より前に定義された他のマクロを呼び出すこともできます。自身を再帰的に呼び出すことはできません。
バインディング
バインディングは、要素のプロパティを式にリンクする、[property]
の形式の特別な属性です。代わりに、XML 互換の構文を data-amp-bind-property
の形式で使用することもできます。
状態 が変わると式が再評価され、バインドされた要素のプロパティが新しい式の結果で更新されます。
amp-bind
は、以下の 4 種類の要素の状態に基づいてデータ バインディングをサポートします
種類 | 属性 | 詳細 |
---|---|---|
Node.textContent | [text] | ほとんどのテキスト要素でサポートされています。 |
CSS クラス | [class] | 式の結果がスペース区切りの文字列である必要があります。 |
hidden 属性 | [hidden] | ブール式である必要があります。 |
AMP 要素のサイズ | [width] [height] | AMP 要素の幅または高さ(あるいはその両方)を変更します。 |
要素固有の属性 | 各種 |
バインディングに関する注意事項:
- セキュリティ上の理由から、
innerHTML
へのバインドは禁止されています。 - 属性のバインディングは、安全でない値(
javascript:
など)の場合はすべてサニタイズされます。 - ブール式の結果によってブール値の属性が切り替えられます。たとえば、
<amp-video [controls]="expr"...>
では、expr
の評価結果がtrue
の場合、<amp-video>
要素にcontrols
属性が設定されます。expr
の評価結果がfalse
の場合、controls
属性が削除されます。 - DOM API で XML(XHTML、JSX など)や属性を書き込む場合に、属性名の角かっこ(
[
と]
)が問題になることがあります。このような場合は、構文に[x]="foo"
ではなくdata-amp-bind-x="foo"
を使用します。
要素固有の属性
以下のコンポーネントと属性へのバインディングのみが許可されています。
コンポーネント | 属性 | 動作 |
---|---|---|
<amp-brightcove> | [data-account] [data-embed] [data-player] [data-player-id] [data-playlist-id] [data-video-id] | 表示される Brightcove 動画を変更します。 |
<amp-carousel type=slides> | [slide] * | 現在表示されているスライドのインデックスを変更します。例をご覧ください。 |
<amp-date-picker> | [min] [max] | 選択可能な最も古い日付を設定します。 選択可能な最も新しい日付を設定します。 |
<amp-google-document-embed> | [src] [title] | 更新された URL のドキュメントを表示します。 ドキュメントのタイトルを変更します。 |
<amp-iframe> | [src] | iframe のソース URL を変更します。 |
<amp-img> | [alt] [attribution] [src] [srcset] | [src] にバインドする場合、キャッシュ上でバインドを行うには、[srcset] にもバインドします。対応する amp-img 属性をご確認ください。 |
<amp-lightbox> | [open] * | ライトボックスの表示を切り替えます。ヒント: ライトボックスが閉じているときに変数を更新するには、on="lightboxClose: AMP.setState(...)" を使用します。 |
<amp-list> | [src] | 式が文字列の場合は、JSON を文字列の URL から取得してレンダリングします。式がオブジェクトまたは配列の場合は、式データをレンダリングします。 |
<amp-selector> | [selected] *[disabled] | 現在選択されている子要素を変更します。 子要素は option 属性の値で特定できます。値のカンマ区切りのリストから複数の値を選択できます。例をご覧ください。 |
<amp-state> | [src] | JSON を新しい URL から取得して既存の状態にマージします。次の更新では、サイクルの防止のために <amp-state> 要素が無視されます。 |
<amp-video> | [alt] [attribution] [controls] [loop] [poster] [preload] [src] | 対応する amp-video 属性をご確認ください。 |
<amp-youtube> | [data-videoid] | 表示される YouTube 動画を変更します。 |
<a> | [href] | リンクを変更します。 |
<button> | [disabled] [type] [value] | 対応する button 属性をご確認ください。 |
<details> | [open] | 対応する details 属性をご確認ください。 |
<fieldset> | [disabled] | フィールドセットを有効または無効にします。 |
<image> | [xlink:href] | 対応する image 属性をご確認ください。 |
<input> | [accept] [accessKey] [autocomplete] [checked] [disabled] [height] [inputmode] [max] [maxlength] [min] [minlength] [multiple] [pattern] [placeholder] [readonly] [required] [selectiondirection] [size] [spellcheck] [step] [type] [value] [width] | 対応する input 属性をご確認ください。 |
<option> | [disabled] [label] [selected] [value] | 対応する option 属性をご確認ください。 |
<optgroup> | [disabled] [label] | 対応する optgroup 属性をご確認ください。 |
<select> | [autofocus] [disabled] [multiple] [required] [size] | 対応する select 属性をご確認ください。 |
<source> | [src] [type] | 対応する source 属性をご確認ください。 |
<track> | [label] [src] [srclang] | 対応する track 属性をご確認ください。 |
<textarea> | [autocomplete] [autofocus] [cols] [disabled] [maxlength] [minlength] [placeholder] [readonly] [required] [rows] [selectiondirection] [selectionend] [selectionstart] [spellcheck] [wrap] | 対応する textarea 属性をご確認ください。 |
* バインド可能な属性を示します(同等のバインド不可能な属性はありません)。
デバッグ
開発中に警告とエラーに焦点を当てたり、特殊なデバッグ関数を使用したりするには、開発モードで(URL フラグメント #development=1
を使用して)テストを実施します。
警告
開発モードでは、バインドされた属性のデフォルト値が対応する式の初期結果と一致しない場合、amp-bind
によって警告が発行されます。これにより、他の状態変数の変化に伴って生じる意図しない変化を防止できます。以下に例を示します。
<!-- 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>
開発モードでは、未定義の変数またはプロパティの参照を解除した場合にも、amp-bind
によって警告が発行されます。これにより、null
式の結果が原因の意図しない変化も防止できます。以下に例を示します。
<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">Some placeholder text.</p>
エラー
amp-bind
の使用時に発生する可能性があるランタイム エラーにはさまざまな種類があります。
種類 | メッセージ | アドバイス |
---|---|---|
無効なバインディング | <P> 要素の [someBogusAttribute] にはバインドできません。 | ホワイトリストに登録されているバインディングのみを使用してください。 |
構文エラー | 式のコンパイル エラーです。 | 式に入力ミスがないか確認してください。 |
ホワイトリストに登録されていない関数 | alert はサポート対象の関数ではありません。 | ホワイトリストに登録されている関数のみを使用してください。 |
サニタイズされた結果 | 「javascript:alert(1)」は [href] の有効な結果ではありません。 | 禁止されている URL プロトコルや式を使用しないでください。AMP 検証ツールでエラーが発生します。 |
CSP 違反 | 次のコンテンツ セキュリティ ポリシーのディレクティブに違反しているため、「blob:...」からのワーカーの作成を拒否されました。 | default-src blob: をオリジンのコンテンツ セキュリティ ポリシーに追加してください。コストのかかる作業が amp-bind によって専用のウェブワーカーに委任されるため、優れたパフォーマンスを実現できます。 |
状態のデバッグ
AMP.printState()
を使用して、現在の状態をコンソールに出力します。
付録
<amp-state>
の仕様
amp-state
要素には、子 <script>
要素、またはリモートの JSON エンドポイントの CORS URL を含む src
属性のいずれかを含めることができます。両方を含めることはできません。
<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>
XHR のバッチ処理
AMP では、JSON エンドポイントに対する XMLHttpRequest(XHR)をバッチ処理します。つまり、AMP ページでは、複数のコンシューマー(複数の amp-state
要素など)のデータソースとして単一の JSON データ リクエストを使用できます。たとえば、amp-state
要素によってエンドポイントへの XHR が作成された場合、XHR の送信中は、同じエンドポイントに対する後続の XHR はトリガーされず、代わりに 1 つ目の XHR の結果を返します。
属性
src | amp-state を更新する JSON を返すリモート エンドポイントの URL。この属性には CORS HTTP サービスを指定する必要があります。 src 属性では、標準的な URL 変数の置換をすべて使用できます。詳しくは、置換ガイドをご覧ください。 エンドポイントは、AMP の CORS リクエストの仕様で指定されている要件を満たしている必要があります。
|
credentials(オプション) | Fetch API で指定されているとおりに credentials オプションを定義します。
include を渡します。この値が設定されている場合、レスポンスは AMP CORS セキュリティ ガイドラインに準拠する必要があります。 |
AMP.setState()
によるディープマージ
AMP.setState()
が呼び出されると、amp-bind
により、指定されたオブジェクト リテラルと現在の状態がディープマージされます。繰り返しマージされるネストされたオブジェクトを除き、オブジェクト リテラルの変数はすべて、状態に直接書き込まれます。状態に含まれているプリミティブと配列は必ず、オブジェクト リテラル内の同じ名前の変数によって上書きされます。
次の例をご覧ください。
{
<!-- 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>
つ目のボタンを押すと、状態が次のように変わります。
{
employee: {
name: 'John Smith',
age: 47,
vehicle: 'Car',
}
}
つ目のボタンを押すと、amp-bind
により、オブジェクト リテラルの引数 {employee: {age: 64}}
が既存の状態に繰り返しマージされます。
{
employee: {
name: 'John Smith',
age: 64,
vehicle: 'Car',
}
}
employee.age
は更新されていますが、employee.name
キーと employee.vehicle
キーは変更されていません。
循環参照を含むオブジェクト リテラルを指定して AMP.setState()
を呼び出すと、amp-bind
によってエラーがスローされます。
変数の削除
既存のステータス変数を削除するには、AMP.setState()
でその値を null
に設定します。前の例の状態から開始して、次のボタンを押します。
<button on="tap:AMP.setState({employee: {vehicle: null}})"...></button>
状態が次のように変わります。
{
employee: {
name: 'John Smith',
age: 48,
}
}
同様に、次のボタンを押します。
<button on="tap:AMP.setState({employee: null})"...></button>
状態が次のように変わります。
{
<!-- State is empty -->
}
式の文法
amp-bind
の式の文法は BNF に似ています。
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
このドキュメントを何度読み返しても、ご質問のすべてを完全に解消することができませんか?他にも同じ事を感じた人がいるかもしれません。Stack Overflow で問い合わせてみましょう。
Stack Overflow にアクセスする バグや不足している機能がありますか?AMP プロジェクトでは皆さんの参加と貢献を強くお勧めしています!当社はオープンソースコミュニティに継続的にご参加いただくことを希望しますが、特に熱心に取り組んでいる問題があれば1回限りの貢献でも歓迎します。
GitHub にアクセスする