blog.bouzuya.net

2015-09-26 いまどきのフロントエンド環境構築にいった & Polymer で HTML の bind にハマった

いまどきのフロントエンド環境構築

いまどきのフロントエンド環境構築』に参加した。

……とは言え、もくもくと Polymer を試していただけなのだけれど。

今週は 2015-09-20 (記事は 2015-09-21) の Polymer コードラボ以来、既存の Polymer Elements を読んでみた (2015-09-22) り、動画 Polycasts を観た (2015-09-23) りと Input ばかりでここまできた。……で、ようやく Output に目を向けたというわけ。

Polymer で HTML を bind する

そして例によってハマる。

Polymer で HTML 文字列を bind しようとしてハマった。

Polymer は Mustache のような記法で property を binding できる。{{property}} のようなイメージだ。AngularJS や Vue.js でもおなじみの記法なのでそれ自体は問題ない。例によってこの記法は HTML escape される。つまり <b>&lt;b&gt; のように出力されて HTML ではなくただのテキストとして binding される。

今回やりたいことの中に HTML として返されたものをそのまま埋め込むというものがあった。

この HTML escape に関してはたいてい抜け道が用意してある。たとえば Mustache では {{{html}}} とすれば HTML escape されずに出力されるし、AngularJS では $sce で安全であるとマークして ng-bind-html を使うし、Vue.js では v-html を使う。だが Polymer にはない。

Polymer には HTML escape されない方法がない。なので自身で JavaScript で要素を取得して innerHTML に設定するしかない。しかし、ここに落とし穴 (ハマりどころ) がある。

解決済みのコードを示す。

_descriptionChanged: function() {
  var html = this.description;
  var el = this.$.description;
  Polymer.dom(el).innerHTML = html; // 落とし穴
}

this.description はこのインスタンスの property だ。Polymer は this.$.<id> で指定した id の要素を取得してくれる。ポイントは Polymer.dom(el) の部分だ。

このコードは Polymer.dom(el).innerHTMLel.innerHTML としてもだいたい動くが問題はある。だいたいといったのは要素自体は挿入されるからだ。問題の例は <dom-module> 内の <style> が効かないことだ。おそらく shady DOM の実装上、CSS を外部から隔離するために DOM にいろいろと細工している。なので生の DOM を変更してしまうとまずい。

解決策はさきに書いたように Polymer.dom(el) で wrap してやる。

ぼくは Polymer.dom の存在は知っていたのだけど、てっきり this.$.<id> で取得できるものは既に wrap されていると思っていた。効率の都合なのかな。