Help us understand the problem. What is going on with this article?

Polymerドキュメント(日本語) Data system/Data binding 〜データバインディング〜

More than 3 years have passed since last update.

目次へ移動

翻訳ドキュメントの管理ページ

データバインディングは、カスタム要素(ホスト要素)のデータとそのローカル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

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

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

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

  • 一方、属性にバインドするには、次のように$に続けて属性名を記述します。
  <a href$="{{hostProperty}}">

この例では、a要素のhref属性にバインドしています。

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

バインディングアノテーション

二つの中括弧({{ }})または、二つの角括弧([[ ]])で囲まれたテキスト。ホストのデータがバインドされていることを表します。

複合バインディング(compound binding)

一つ以上のバインディングアノテーションを含む文字列リテラル。

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

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

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

ターゲットプロパティへバインド

ターゲットプロパティにバインドするには、属性値に[アノテーション](#binding-annotations

)または複合バインディングを使用して、プロパティに対応する属性名を指定します。

<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-*へバインドする場合には、属性のバインド構文を使ってください。詳細は、ネイティブ要素の属性へのバインドを参照してください。

テキストコンテンツ(text contetent)にバインド

ターゲット要素の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>

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

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

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

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

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

論理否定演算子(!)

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

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

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

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

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

算出バインディング(Computed bindings)

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

<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)まで算出関数が呼び出されることはありません

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

算出バインディングへのリテラルな引数

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

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

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

例:

<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>

複合バインディング(Compound bindings)

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

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

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

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

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

配列と配列アイテムへのバインディング

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

<!-- Don't do this! -->
<span>{{array[0]}}</span>
<!-- Or this! -->
<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は双方向データバインディングを実現するために、イベントの命名規則を利用しています。

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

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要素上で標準の通知プロパティにバインドする場合、イベント名の指定は必須ではありません。デフォルトの命名規則のように、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}}">

移動したセクション

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away