こんにちは、らこです。Web ComponentsのCustom Elementsでアレする話をします。
Polymer is large
Custom ElementsといえばPolymerみたいな風潮出来始めてますよね。Polymerはほんとうに良く出来てると思いますし、Polyfillに関しては言うこと無いと思います。ですが、本質的に自分が何を達成したいのか考えるとPolymerは大きすぎるような気がしました。
やりたいこと=HTMLの分割
Custom Elementsを使って何をしたいかというと私はHTMLを分割したいだけです。独自タグを目印に別のHTMLを注入できればそれで満足です。そこにPolymerは必要なくて、ただWeb Componentsのpolyfillがあればいいだけだと気づいたので自分で作ることにしました。
生のDartで実装する
というわけで、Custom ElementsとHTML Importsもどきを実装してみました。
エントリポイントはこちら
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>custom_elements_dart</title>
<script src="packages/web_components/webcomponents.min.js"></script>
</head>
<body>
<sample-element>
</sample-element>
<script type="application/dart">
import 'dart:html';
import 'package:custom_elements_dart/sample_element.dart';
void main() {
document.registerElement("sample-element", SampleElement);
}
</script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
エントリポイントでやってるのは、sample-element
タグの登録だけです。
document.registerElement(String tagName, Type htmlElementType);
を呼び出すことで、タグ名と、それに対応するHtmlElement
型のクラスをバインドします。
<div>
<h1 id="greeting"></h1>
<p>This is sample element.</p>
<input id="nameInput" type="text"/>
</div>
読み込まれるHTMLです。#greeting
と#nameInput
を定義してます
library sample_element;
import "dart:html";
import 'dart:async';
import "package:mustache/mustache.dart" as mustache;
class SampleElement extends HtmlElement {
SampleElement.created() :super.created(){
onInput.matches("#nameInput").listen((e) {
var name = (e.matchingTarget as TextInputElement).value;
_greet(name);
});
getTemplate().then((template) {
appendHtml(template);
_greet("World");
});
}
String _template;
Future<String> getTemplate() {
if (_template != null) {
return new Future.value(_template);
}
return HttpRequest.getString("packages/custom_elements_dart/sample_element.html")
.then((v) => _template = v);
}
_greet(String name) {
if (name == null) {
name = "World";
}
var greeter = mustache.parse("Hello {{name}}!");
var output = greeter.renderString(
{
"name": name
});
this.querySelector("#greeting").innerHtml = output;
}
}
追加した<sample-element>
の中身です。
SampleElement.created() :super.created(){
onInput.matches("#nameInput").listen((e) {
var name = (e.matchingTarget as TextInputElement).value;
_greet(name);
});
getTemplate().then((template) {
appendHtml(template);
_greet("World");
});
}
HtmlElement.created()
はHtmlElement
クラスのコンストラクタで、継承する際に必ず実装しないといけません。まず#nameInput
要素からonInput
イベントを拾って、ビューを更新するリスナを登録してます。
次にテンプレートHTMLを読み込んで、自身にappendした後に最初のビュー更新をかけています。
String _template;
Future<String> getTemplate() {
if (_template != null) {
return new Future.value(_template);
}
return HttpRequest.getString("packages/custom_elements_dart/sample_element.html")
.then((v) => _template = v);
}
ここがHTML Importもどきの部分です。Getリクエストで非同期にテンプレートHTMLを取得します。取得後はキャッシュしています。今思うとこれstaticにしたほうがいいですね。
_greet(String name) {
if (name == null) {
name = "World";
}
var greeter = mustache.parse("Hello {{name}}!");
var output = greeter.renderString(
{
"name": name
});
this.querySelector("#greeting").innerHtml = output;
}
ビュー更新部分です。あいさつのテンプレートをMustacheでパースして、与えたname
を食わせています。文字列が出来上がったら#greeting
要素に流し込んでいます。
こんな感じ
テキストボックスの変更が反映されます。MV*っぽい動きです。
Polymer無くてもこれくらいはできる
結局Polymerもやってることはこれの応用というか、Dartでできることしかやってないので、当然ですがこのくらいはできます。ライブラリに頼るのもいいですが、自分でやるのも楽しいです。
めちゃくちゃ簡単
見てもらってわかるようにDartの場合、HTML要素とクラスが1:1対応してるので、めちゃくちゃ簡単に、わかりやすくCustom Elements使えます。HtmlElementを継承したクラスをregisterElementするだけです。冗談みたいに簡単に使えるのでJSerの人もCustom Elementsで遊ぶためだけにDartやってほしいくらいです。
以上DartでCustom Elements作る方法でした。Polymerもいいけど自作もね。
いよいよ次で最後ですね。最終日も私です。よろしくお願いします。