AngularとPolymerが共存する世界への第一歩


# はじめに
[Polymer 1.0](https://www.polymer-project.org/1.0/)がリリースされました。WebComponentsの普及のためにPolymerには頑張ってもらわないといけませんが、そのためには現在Webで大きなシェアを占めるフレームワーク、AngularJSとの共存が不可欠です。その共存の理想と現実、未来について個人的な考えをまとめます。

# そもそも対立構造ではない
何故か一部のプログラマの中では「AngularJS vs Polymer」になっているような空気を時々感じますが、本来PolymerはAngularJSと競合する技術スタックではありません。
Polymer、というよりWebComponents(特にCustom Element)はAngularJSやその他のMVCフレームワークよりも低いレイヤーのWeb技術で、むしろ共存できなければならないものです。

# Polymer+Angularのあるべき姿
Polymerが提供するのはCustom Elements(独自タグ)の定義を簡単にするAPIです。PolymerのAPIを利用して定義された独自タグはWebComponents仕様に準拠しているがゆえに、同じWebComponents技術を利用するレイヤー上では再利用可能です。この再利用性を高めるのにWebComponentsのHTML Imports技術が大きく寄与しています。

```html
<!DOCTYPE html>
<html>
  <head>
    <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <link rel="import" href="dom-element.html">
  </head>
  <body>
    <dom-element></dom-element>
  </body>
</html>
```

WebComponentsのAPIにより新たに定義されたタグは、アプリケーション側から見た時にHTML5標準のタグと何か差があるかというと、無い、あってはならないというのが理想です。
ここでAngularが`<dom-element>`にバインディングを行うのと、`<div>`にバインディングを行うのに、なにか違いがあってはならないのです。

# Polymer+Angularの現在
とはいえ、未だPolymerとAngularの世界は重なっておらず、`ng-model`はPolymerの世界には通用しません。

次のPolymerElement(Polymerにより定義されたCustom Element)`<input-binding>`はinput要素のvalueをバインドして表示するだけの要素です

```html
<link rel="import" href="../bower_components/polymer/polymer.html"/>
<link rel="import" href="../bower_components/paper-input/paper-input.html"/>
<link rel="import" href="../bower_components/iron-input/iron-input.html"/>

<dom-module id="input-binding">
  <template>
    <paper-input-container>
      <input is="iron-input" bind-value="{{text}}"
             placeholder="text">
    </paper-input-container>
    <strong>{{text}}</strong>
  </template>
</dom-module>

<script>
  Polymer({
    is: "input-binding",
    properties: {
      text: {
        type: String,
        notify: true
      }
    }
  });
</script>
```

PolymerElementのテンプレート内部でinput要素の`value`にバインドするには`bind-value`要素にプロパティをセットします。PolymerElementが持つプロパティは`properties`で定義され、`text`プロパティは`String`型で、外部に変更通知を`notify`することが記述されています。

一方、AngularJSでinputの値をバインドするには`ng-model`を使うのが一般的です。

```html
  <input style="width: 100%" type="text" ng-model="text"/>

  <p>{{ text }}</p>
```

ここで、`<input-binding>`の`text`をAngularでバインドし、`<p>`に表示したいときどうしたらいいでしょうか？

```html
  <input-binding text="{{ text }}"></input-binding>

  <p>{{ text }}</p>
```

一見双方向バインディングされているように見えますが、PolymerElementに対するフックがAngularJSには(当然ながら)定義されていないので、`text`属性が`<input-binding>`の内部から書き換えられたとしてもそのイベントは`{{text}}`に伝播することはありません。
ですが、PolymerElementが何も情報を発していないわけではなく、PolymerはPolymer語で、
「`text`プロパティが変更されたら`text-changed`イベントを発火する」という仕事をしています。

必要なのはPolymer語のイベントを聞き取り、Angular語に翻訳する「通訳」です。

# Directiveを使って通訳する
一つの解決策として、Polymer語を翻訳するDirectiveを定義してみました。`polymer-attr`属性をPolymerElementに付与することで、内部の変更をAngularのScopeに反映します。

```js
app.directive("polymerAttr", function () {
  return {
    restrict: 'A',
    link: function (scope, element, attr) {
      var eventName = attr.polymerAttr + '-changed';
      element.on(eventName, function (event) {
        scope[attr.polymerAttr] = event.detail.value;
        scope.$apply();
      });
    }
  };
});
```

これは`<property>-changed`に関してはすべてのPolymerElementに適用できる汎用のDirectiveです。

これを付与し、`text`プロパティの変更をAngular側で受け取ります。


```html
  <input-binding text="{{ text }}" polymer-attr="text"></input-binding>
  <p>{{ text }}</p>
```

これによってアプリケーション開発者は`polymer-attr`を使うだけで、PolymerとAngularの垣根なく双方向バインディングを行うことが可能になりました。

# Polymer+Angularの今後
今回挙げた「通訳」の例はたったひとつのイベントに対する解決策であり、他にもいろいろなDSLに対する通訳が必要ですが、それでもこれだけの記述でPolymerとAngularの世界をつなぐことができることがわかりました。

Polymerは本質的にはWebComponentsのpolyfillであり、糖衣構文です。Polymerは1.0になりましたが、そのAPIの使いやすさは正直なところそれほど高くないです。しかし競争相手となるライブラリがMozillaのX-Tagくらいしかなく、X-TagはPolymer以上に使いにくいAPIなので困ったものです。
ですが、この状態は本来WebComponentsと親和性の高いES6のclass記法が使えるようになれば大きく変わることが期待できます。

WebComponentsが普及した先では、再利用可能なタグを開発し配布するコンポーネントエンジニアと、それを利用するアプリケーションエンジニアに二分され、後者はそれがHTML5のタグなのか自分でインポートしたタグなのか気にすることなく自由にアプリケーションを開発できるようになります。この構図は自分がインストールしたjQueryプラグインがjQuery標準のAPIと同じように扱えるのと全く同じです。そして、AngularJSが今ではjQueryと共存できているように、WebComponentsもいずれはもっと自然にAngularと共存できるようになるはずです。具体的には、Angular2のコンポーネントシステムがWebComponentsに準拠することでPolymerとAPIを共通化できるのではないかと考えています。

# まとめ
WebComponents技術は今後必ずWebアプリケーション開発の中で重要なパーツになります。その取っ掛かりとしてPolymerに今から触れておくのは決して無駄な投資にはならないでしょう。
「Angularを使っているから」と避けていた方は1.0リリースをきっかけに手を出してみてはどうでしょうか？