Polymer で x-ahomu 要素をつくった

デモをiframeに食わせてるだけなので、モバイルだとよく分からない状態だと思われる。ソーリー。要素のインスペクトすると、ちゃんと <x-ahomu> があるのを確認できるはず。

<x-ahomu>

去年Googleのイベントで話を伺ってから、Web Components/Polymer に対しては好意的なつもりなので、改めて試してみた。要素としての設計はやっつけだけど、実装の手触りだけ。

アッサリとした構成の x-imager を参考に作ったのだけれど、ボイラープレートはpolymer-boilerplateなので、そっちで作った方がしっかりしてるようには見えそう。

以下、実装時の適当なメモなど。

例えば polymer-ajax

<polymer-ajax url="https://api.github.com/users/ahomu/repos" auto handleAs="json" response="{{repos}}"></polymer-ajax>

polymer-ajax.htmlのdocコメントを見ると、使い方がなんとなくわかる。attributes= で示されているプロパティは、polymer-ajax要素を書くときの属性値で上書きできる。

ここでは、自分のGitHubリポジトリのAPIを叩いて、それを handleAsでJSONとして扱い、レスポンスを {{repos}} にバインディングしている。

polymer-ajax が XMLHttpRequestまわりの機能要素を分化して polymer-xhr を内包しているのも興味深い。

ライフサイクルコールバック

<script>
Polymer('x-ahomu', {
  created : function() { /* インスタンスが生成された直後のとき */ },
  ready   : function() { /* ShadowDOMとか要素の準備ができたとき */ },
  attached: function() { /* 要素がドキュメントに追加されたとき */ },
  detached: function() { /* 要素がドキュメントから削除されたとき */ }
});
</script>

要素のライフサイクルにおける各タイミングでは、要素に定義したコールバックが呼び出される。 ready でアレコレしとけば困ること少なそう。

イベント

<template>
  <button on-click="{{changeColor}}" change-to-color="mono">Mono</button>
  <button on-click="{{changeColor}}" change-to-color="rainbow">Rainbow</button>
  <button on-click="{{changeColor}}" change-to-color="green">Green</button>
</template>

<script>
Polymer('x-ahomu', {
  // 前略
  changeColor: function(e) {
    this.color = e.currentTarget.getAttribute('change-to-color');
  },
  // 以下略
});
</script>

DOMイベントはこんな感じで、要素に定義したハンドラをバインディングできる。よくあるMVVM風味。

ここではイベントの発生によって this.color を変更しているが、属性値の変更は attributeChanged のコールバックで捉えられる。

ユーザーアクションを要素の外側にイベントを通して作用させるには、this.fire(eventName, dataObj) で発火させることで、外側から addEventListener で捉えられる。

Filter とか <template> とか

<template>
  <ul>
    <template repeat="{{ repo in repos | rejectForkedRepos }}">
    <li><a href="{{repo.html_url}}">{{repo.name}}</a></li>
    </template>
  </ul> 
</template>

<script>
Polymer('x-ahomu', {
  // 前略
  rejectForkedRepos: function(repos) {
    repos = repos || [];
    return repos.filter(function(r) { return r.fork === false });
  },
  // 以下略
});
</script>

さすがにAngularJSと酷似する趣になるが、Filterを実装することができる。データバインディングに対するFilterって、機能的に必要なのは分かるけど、うーんPolymerとしての便利機能だからいいのかな。

実装メモはここまで。

Polymerのサイトが新しくなっていた

大体このあたりのドキュメントを読めば、振る舞いを実装すること自体はすぐにできるはず。ただし、実際にPolymerで実装すべき要素に思いを馳せて作ろう思うと、グローバルに再利用可能な振る舞い・カプセル化を実装するためにいくつか意識すべき点がありそう。

  • Functional. The browser already knows what to do with a <select> element. When it encounters <select> in markup it creates an interactive control for the user.
  • Reusable. The <select> element is a reusable package of functionality that you don’t have to implement yourself.
  • Interoperable. Every JavaScript library knows how to interact with DOM elements.
  • Encapsulated. It keeps its internals all tucked away, so including one won’t break the rest of your page.
  • Configurable. You can configure its behavior with HTML attributes, without using any script.
  • Programmable. If you grab the element from the DOM it also has methods and properties for things that don’t make sense in markup.
  • Event Generator. It dispatches events to let you know when something interesting happens.
  • Composable. Not only can you include a <select> inside of most other kinds of element, its behavior can also change depending on which things you put inside of it. Understanding Polymer - Polymer

上記の引用における、ファンクショナル・再利用性・相互運用性・カプセル化・設定可能性・プログラマブル・イベント生成・コンポーザブル、という要素を要素たらしめる特徴の話。

このあたりまでくるとComponentのことも気になってくるが、Componentは直近の現実解になる思想の実装であり、WebComponents/Polymerは将来的な方向性を提案する実装だと思うので、時系列の中で適当に棲み分けされるだろう。

ということで試してみたシリーズでした。