データバインディングは、Custom Element(ホストエレメント)のデータとそのローカルDOM(子エレメントまたはターゲットエレメント)のプロパティまたは属性にコネクトします。ホストエレメントのデータは、データパスで表されたプロパティやサブプロパティまたは、一つ以上のパスに基づき生成されたデータになります。

エレメントのローカルDOMのテンプレートにアノテーションを追加することで、データバインディングを生成できます。

<dom-module id="host-element">
  <template>
    <target-element target-property="{{hostProperty}}"></target-element>
  </template>
</dom-module>

データバインディングのアップデートは、プロパティエフェクトの一つです。

データバインディングは、ローカルDOMのテンプレートにHTML属性として現れます。:

property-name=annotation-or-compound-binding
attribute-name$=annotation-or-compound-binding

バインディングの左側で、ターゲットのプロパティや属性を識別します。

  • プロパティにバインドするには、プロパティ名と属性名のマッピングで説明した通り、プロパティ名は属性のフォーマット(camelCaseでなくdash-case)で指定して下さい。

    <my-element my-property="{{hostProperty}}">
    

    この例では、<my-element>上のプロパティmyPropertyをターゲットにしてバインドします。

  • 一方、属性にバインドするには、次のように$に続けて属性名を記述します。

    <a href$="{{hostProperty}}">
    

    この例では、aエレメントのhref属性にバインドしています。

バインディングの右側は、 バインディングアノテーション(binding annotation) または 複合バインディング(compound binding) のいずれかになります。:

バインディングアノテーション
二つの中括弧({{ }})または、二つの角括弧([[ ]])で囲まれたテキスト。ホストのデータがバインドされていることを表します。
複合バインディング(compound binding)
一つ以上のバインディングアノテーションを含む文字列リテラル。

データフローが、ホストからターゲットに下に向けて流れるか、ターゲットからホストへ上に向けて流れるか、あるい双方向になるかは、バインディングアノテーションの種類やターゲットプロパティの設定によってコントロールされます。

  • 二重中括弧({{ }})は、上向きと下向きのデータフロー両方をサポートします。

  • 二重角括弧([[ ]])は、下向きに一方向のデータフローだけをサポートします。

データフローの詳細については、データフローの制御方法を参照してください。

ターゲットプロパティにバインドするには、属性値にアノテーションまたは複合バインディングを使用して、プロパティに対応する属性名を指定します。:

<target-element name="{{myName}}"></target-element>

この例では、ターゲットエレメントのnameプロパティをホストエレメントのmyNameプロパティにバインドしています。アノテーションには双方向又は自動(automatic)デリミタ({{ }})が使用されているので、nameプロパティでサポートするように設定していれば双方向バインディングが生成されます。

一方向バインディングを指定するには、二重角括弧([[ ]])を使用します。:

<target-element name="[[myName]]"></target-element>

プロパティ名は、プロパティ名と属性名のマッピングで説明しているように、属性のフォーマットで記述されます。エレメントのcamelCaseプロパティをバインドするには、属性名にdash-caseを使用します。 例えば:

<!-- Bind <user-view>.firstName to this.managerName; -->
<user-view first-name="{{managerName}}"></user-view>

バインドされているプロパティがオブジェクトか配列の場合、どちらのエレメントも同じオブジェクトへの参照を取得します。つまり、どちらのエレメントからもオブジェクトを変更できるので、真の一方向バインディングは実現できません。詳細は、オブジェクト及び配列のデータフローを参照してください。

属性やプロパティの中には特別なものがあります。stylehrefclassfordata-*属性へバインドする場合には、属性のバインド構文を使ってください。詳細は、ネイティブエレメントの属性へのバインドを参照してください。

ターゲットエレメントのtextContentにバインドするには、ターゲットエレメントの中にアノテーションまたは複合バインディングを含めるだけです。

<dom-module id="user-view">
  <template>
    <div>[[name]]</div>
  </template>

  <script>
    class UserView extends Polymer.Element {
      static get is() {return 'user-view'}
      static get properties() {
        return {
          name: String
        }
      }
    }

    customElements.define(UserView.is, UserView);
  </script>
</dom-module>

<!-- usage -->
<user-view name="Samuel"></user-view>

テキストコンテンツへのバインディングは、常にホストからターゲットへ一方向に行われます。

大半のケースでは、データを他のエレメントへバインドするのに、プロパティバインディングを利用するべきです。そうすることで、エレメント上のJavaScriptのプロパティに新しい値を設定すると、その変更が伝播されます。

しかし、時にはエレメントにプロパティではなく属性を設定する必要があるかもしれません。例えば、CSSで属性セレクタを利用していたり、ARIA(※)のような属性ベースのAPIと相互運用性を高めるために属性セレクタを使っている場合です。 (※)ARIA(Accessible Rich Internet Applications):ハンディキャップを持つ人に向けた情報のアクセシビリティ向上を目的とした標準規約

属性にバインドするには、属性名の後にドル記号($)を追加します。:

<div style$="color: {{myColor}};">

属性の値は、バインディングアノテーションまたは複合バインディングのいずれかになります。

属性バインディングにより、次の呼び出しが行われます。:

element.setAttribute(attr,value);

以下とは対照的です。:

element.property = value;

例:

<template>
  <!-- Attribute binding -->
  <my-element selected$="[[value]]"></my-element>
  <!-- results in <my-element>.setAttribute('selected', this.value); -->

  <!-- Property binding -->
  <my-element selected="{{value}}"></my-element>
  <!-- results in <my-element>.selected = this.value; -->
</template>

属性へのバインディングは、ホストからターゲットに向けて常に一方向なります。値は、属性のシリアル化で説明したように、 現在の 型に応じてシリアライズされます。

繰り返しになりますが、属性にバインドする際は、値は文字列にシリアライズする必要があるので、単純なデータの伝播には、常にプロパティバインディングを使用する方が優れたパフォーマンスを発揮します。

Polymerが直接データをバインドできない一般的なネイティブエレメントのプロパティがわずかながら存在します。原因は、いくつかのブラウザ上でバインディングが引き起こす問題にあります。

以下のプロパティにバインディングの効果を与えるには属性バインディングを使用する必要があります。:

属性 プロパティ 説明
class classList, className フォーマットの異なる二つのプロパティへマップされます。
style style 仕様では、styleCSSStyleDeclarationオブジェクトに対するリードオンリーの参照とみなされます。
href href
for htmlFor
data-* dataset カスタムデータ属性(属性名がdata-で始まる)はdatasetプロパティに保存されています。
value value <input type="number">だけに使えます。

注意valueプロパティへのデータバインディングは、入力タイプが数値の場合IEではうまく機能しません。このようなケースでは、一方向の属性バインディングを使用することで、valueに数値入力を設定できます。あるいは、双方向バインディングを正しく扱うiron-inputpaper-inputのような別のエレメントを使用して下さい。

上記リストには現在、プロパティのバインディングで問題を引き起こすことが知られているプロパティが含まれます。他のプロパティも影響を受ける可能性があります。

プロパティをバインドできない理由は様々です:

  • これら属性値に括弧{{...}}を記述する機能には全てのブラウザにおいて問題がある。

  • (classのように)別の名前のJavaScriptプロパティにマップされる属性がある。

  • (styleのように)固有の構造を持つプロパティが存在する。

動的な値への属性バインディング($=を使用):

<!-- class -->
<div class$="[[foo]]"></div>

<!-- style -->
<div style$="[[background]]"></div>

<!-- href -->
<a href$="[[url]]">

<!-- label for -->
<label for$="[[bar]]"></label>

<!-- dataset -->
<div data-bar$="[[baz]]"></div>

<!-- ARIA -->
<button aria-label$="[[buttonLabel]]"></button>

Polymerは、二種類のデータバインディングデリミタを用意しています。:

一方向デリミタ: [[binding]]
一方向バインディングは、下向きのデータフローのみ許可します。
双方向または自動(automatic)デリミタ: {{binding}}
自動バインディングは、上向きと下向きのデータフローを許可します。

双方向バインディングと上向きデータフローについては、データフローを参照してください。

デリミタ内のテキストは、次のいずれかになります。:

  • プロパティまたはサブプロパティのパス(例:usersaddress.street)。
  • 算出バインディング(例:_computeName(_computeName(firstName, lastName, locale))。
  • 上記のいずれかに、否定演算子(!)を前置したもの

データバインディングアノテーションのパスは、現在のデータバインディングのスコープに関連しています。

バインディングアノテーションの最もシンプルな形式は、ホストプロパティを使用する場合です。:

<simple-view name="{{myName}}"></simple-view>

バインドされるプロパティがオブジェクトか配列の場合、どちらのエレメントも同じオブジェクトへの参照を取得します。つまり、どちらのエレメントからもオブジェクトを変更できるので、真の一方向バインディングは実現できません。詳細については、オブジェクトおよび配列のデータフローを参照してください。

以下に示すように、バインディングアノテーションには、サブプロパティのパスも含めることができます。:

<dom-module id="main-view">

  <template>
    <user-view first="{{user.first}}" last="{{user.last}}"></user-view>
  </template>

  <script>
    class MainView extends Polymer.Element {
      static get is() {return 'main-view'}
      static get properties() {
        return {
          user: Object
        }
      }
    }

    customElements.define(MainView.is, MainView);
  </script>

</dom-module>

サブプロパティの変更は自動的に監視可能(obsevable)ではありません。

ホストエレメントがサブプロパティを更新する場合は、パスによるプロパティまたはサブプロパティの設定で説明した通り、setメソッドを使用するか、Polymerへの通知で説明したnotifyPathメソッドを使用する必要があります。

//  Change a subproperty observably
this.set('name.last', 'Maturin');

バインディングが双方向で、ターゲットエレメントがバインドされたプロパティを更新する場合には、その変更は上に向けて自動的に伝播します。

バインドされているサブプロパティがオブジェクトか配列の場合、どちらのエレメントも同じオブジェクトへの参照を取得します。つまり、どちらのエレメントもオブジェクトを変更できるので、真の一方向バインディングは実現できません。詳細については、オブジェクトおよび配列のデータフローを参照してください。

バインディングアノテーションは、バインディングデリミタの中の最初の文字として、単一の論理否定演算子(!)をサポートします。:

<template>
  <my-page show-login="[[!isLoggedIn]]"></my-page>
</template>

この例では、isLoggedInが真の値を持つならshowLoginfalseになります。

論理否定演算子は一つだけサポートされています。(!!)を使って値の型変換をするようなことはできません。より複雑な変換が必要な場合には、算出バインディングを使用します。

論理否定バインディングは一方向です。: 論理否定演算子を使ったバインディングは、常にホストからターゲットに一方向になります。

算出バインディング算出プロパティと似ています。バインディングアノテーション内で宣言されます。

<div>[[_formatName(first, last, title)]]</div>

算出バインディングの宣言は、算出関数の名前に続けて括弧で囲った依存部のリストを記述します。

エレメントは、自身のテンプレート内で、同じ算出関数を参照する算出バインディングを複数持つことができます。

算出プロパティとコンプレックスオブザーバーでサポートされている依存部のタイプに加えて、算出バインディングの依存部には、文字列または数値リテラルを含めることができます。

算出バインディングが役に立つのは、算出プロパティをエレメントのAPIの一部として公開したり、エレメント内の他の場所で使用する必要がない場合です。算出バインディングは、値を表示する際に、フィルタリングしたり変換したりするのにも役立ちます。

算出バインディングは、以下の点で算出プロパティと異なります:

  • 算出バインディングの依存部は、現在のバインディングスコープに関連づけて解釈されます。例えば、テンプレートリピーターの内部では、依存部のプロパティは現在のitemを参照することになります。

  • 算出バインディングの引数リストには、リテラルな引数を含めることができます。

  • 算出バインディングは空の引数リストを持つことができます。この場合、算出関数の呼び出しは一度限りです。

例:

<dom-module id="x-custom">

  <template>
    My name is <span>[[_formatName(first, last)]]</span>
  </template>

  <script>
    class XCustom extends Polymer.Element {
      static get is() {return 'x-custom'}
      static get properties() {
        return {
          first: String,
          last: String
        }
      }
      _formatName(first, last) {
        return `${last}, ${first}`
      }

    }

    customElements.define(XCustom.is, XCustom);
  </script>

</dom-module>

この場合、spantextContentプロパティは、_formatNameの返り値にバインドされ、firstlastが変更されるたびに再計算されます。

算出バインディングは一方向:算出バインディングは、ホストからターゲットに常に一方向です。

算出バインディングの依存部には、コンプレックスオブザーバーによってサポートされている依存部のタイプであればどれでも含めることができます。

  • 現在のスコープ上のシンプルなプロパティ

  • サブプロパティへのパス

  • ワイルドカードを含むパス

  • 配列のsplicesへのパス

上記に加えて、算出バインディングはリテラルな引数もサポートしています。

依存部のプロパティがどのタイプであっても、算出関数に渡される引数はオブザーバーに渡されるものと同じものになります。

オブザーバーや算出プロパティと同様に、依存部の全てのプロパティが定義される(!=undefined)まで算出関数が呼び出されることはありません

ワイルドカードを含むパスを使った算出バインディングの例は、配列アイテムへのバインドを参照してください。

算出バインディングに渡すことができる引数は、文字列または数値リテラルです。

文字列は、シングルクォテーション(')又はダブルクォテーション(")で囲われるでしょう。属性やプロパティのバインディングにおいて、属性値にダブルクォテーションを使っている場合、文字列リテラルにはシングルクォテーションを使用して下さい。これらは逆であっても構いません。

文字列リテラルでカンマを使用:文字列リテラルの中でカンマを使用する場合には、バックスラッシュ(</code>)を使ってエスケープしなければいけません

例:

<dom-module id="x-custom">
  <template>
    <span>{{translate('Hello\, nice to meet you', first, last)}}</span>
  </template>
</dom-module>

なお、算出バインディングに依存部のプロパティがない場合、評価は一度だけ行われます。:

<dom-module id="x-custom">
  <template>
    <span>{{doThisOnce()}}</span>
  </template>

  <script>
    class XCustom extends Polymer.Element {

      static get is() {return 'x-custom'}

      doThisOnce: function() {
        return Math.random();
      }

    }

    customElements.define(XCustom.is, XCustom);
  </script>
</dom-module>

単一プロパティのバインディングやテキストコンテンツのバインディングに文字列リテラルを混ぜて使用することもできます。例えば:

<img src$="https://www.example.com/profiles/[[userId]].jpg">

<span>Name: [[lastname]], [[firstname]]</span>

複合バインディングは、個々のバインディングのいずれかの値が変更されるたびに再評価されます。undefined値は、空の文字列によって補完されます。

複合バインディングは一方向:複合バインディングでは、一方向([[ ]])または自動({{ }})バインディングアノテーションを使用することができますが、その向きはホストからターゲットに常に一方向になります。

アノテーションの解析(parsing)をシンプルに保つために、Polymerは配列のアイテムに直接バインドする手段を提供していません

<!-- Don't do this! This format doesn't work -->
<span>{{array[0]}}</span>
<!-- Don't do this! Data may display, but won't be updated correctly -->
 <span>{{array.0}}</span>

データバインディングにおいて、配列のアイテムとやりとりする方法はいくつか存在します。:

  • ヘルパーエレメントdom-repeatを使用すると、配列内の各アイテムに対して、テンプレートのインスタンスを作成することができます。dom-repeatのインスタンス内部では、配列アイテムのプロパティへバインドできます。

  • ヘルパーエレメントarray-selectorを使用すると、配列中から選択されたアイテムとデータをバインドできます。そこで選択されるアイテムは、単一のアイテムか、元の配列のサブセットのいずれかになります。

  • 算出バインディングを使って、配列の個々のアイテムをバインドすることができます。

関連トピック:

算出バインディングを使用して、特定の配列アイテムやその配列アイテムのサブプロパティにバインドすることができます(例:array[index].name)。

次の例は、算出バインディングを使用して、配列アイテムからプロパティにアクセスする方法を示しています。 バインディングにワイルドカードパス(myArray.*)を使用しているので、サブプロパティの値が変更された場合や、配列そのものが変更された場合には算出関数を呼び出す必要があります。

<dom-module id="x-custom">

  <template>
    <div>[[arrayItem(myArray.*, 0, 'name')]]</div>
    <div>[[arrayItem(myArray.*, 1, 'name')]]</div>
  </template>

  <script>

    class XCustom extends Polymer.Element {

      static get is() {return 'x-custom'}

      static get properties() {
        return {
          myArray: {
            type: Array,
            value: [{ name: 'Bob' }, { name: 'Doug' }]
          }
        }
      }

      // first argument is the change record for the array change,
      // change.base is the array specified in the binding
      arrayItem(change, index, path) {
        // this.get(path, root) returns a value for a path
        // relative to a root object.
        return this.get(path, change.base[index]);
      },

      ready() {
        super.ready();
        // mutate the array
        this.unshift('myArray', { name: 'Susan' });
        // change a subproperty
        this.set('myArray.1.name', 'Rupert');
      }
    }

    customElements.define(XCustom.is, XCustom);
  </script>

</dom-module>

双方向バインディングは、下向き(ホストからターゲットへ)と上向き(ターゲットからホストへ)双方向にデータの変更を伝播できます。変更を上に向けて伝播させるには、自動データバインディングデリミタ({{ }})を使用し、またターゲットプロパティをnotify: trueに設定する必要があります。詳細については、データフローを参照してください。

配列やオブジェクトのプロパティにバインドする場合、どちらのエレメントも共有された配列やオブジェクトにアクセスして変更を加えることができます。そのような場合は、プロパティエフェクトが上向きに伝播するように、自動バインディングデリミタを使用して下さい。詳細については、オブジェクト及び配列のデータフローを参照してください。

変更通知イベントで説明したように、Polymerは双方向データバインディングを実現するために、イベントの命名規則を利用しています。

Polymer Elementでないエレメントや、このイベント命名規則に従わないネイティブエレメントへ双方向のデータバインドを行うために、次の構文を使いアノテーション内に独自の変更イベント名を指定することができます。:

target-prop="{{hostProp::target-change-event}}"

例:

<!-- Listens for `input` event and sets hostValue to <input>.value -->
<input value="{{hostValue::input}}">

<!-- Listens for `change` event and sets hostChecked to <input>.checked -->
<input type="checkbox" checked="{{hostChecked::change}}">

<!-- Listens for `timeupdate ` event and sets hostTime to <video>.currentTime -->
<video url="..." current-time="{{hostTime::timeupdate}}">

Polymer Element上で標準の通知プロパティにバインドする場合、イベント名の指定は必須ではありませんが、デフォルトの命名規則に沿って、property-changedイベントをリッスンするようになっています。以下の構文は同じことです。:

<!-- Listens for `value-changed` event -->
<my-element value="{{hostValue::value-changed}}">

<!-- Listens for `value-changed` event using Polymer convention by default -->
<my-element value="{{hostValue}}">

次のセクションは、データシステムのコンセプトに移行しました。