Edited at
DartDay 24

DartでCustom Elements

More than 3 years have passed since last update.

こんにちは、らこです。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もどきを実装してみました。

エントリポイントはこちら


web/index.html

<!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型のクラスをバインドします。


lib/sample_element.html

<div>

<h1 id="greeting"></h1>

<p>This is sample element.</p>

<input id="nameInput" type="text"/>
</div>


読み込まれるHTMLです。#greeting#nameInputを定義してます


lib/sample_element

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*っぽい動きです。

customelement.gif


Polymer無くてもこれくらいはできる

結局Polymerもやってることはこれの応用というか、Dartでできることしかやってないので、当然ですがこのくらいはできます。ライブラリに頼るのもいいですが、自分でやるのも楽しいです。


めちゃくちゃ簡単

見てもらってわかるようにDartの場合、HTML要素とクラスが1:1対応してるので、めちゃくちゃ簡単に、わかりやすくCustom Elements使えます。HtmlElementを継承したクラスをregisterElementするだけです。冗談みたいに簡単に使えるのでJSerの人もCustom Elementsで遊ぶためだけにDartやってほしいくらいです。


以上DartでCustom Elements作る方法でした。Polymerもいいけど自作もね。

いよいよ次で最後ですね。最終日も私です。よろしくお願いします。