DartでCustom Elements

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

```html: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`タグの登録だけです。

```dart
  document.registerElement(String tagName, Type htmlElementType);
```

を呼び出すことで、タグ名と、それに対応する`HtmlElement`型のクラスをバインドします。

```html:lib/sample_element.html
<div>
  <h1 id="greeting"></h1>

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

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

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

```dart: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>`の中身です。

```dart
  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した後に最初のビュー更新をかけています。


```dart
  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にしたほうがいいですね。

```dart
  _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](https://qiita-image-store.s3.amazonaws.com/0/20253/ad326ca0-e3c2-855a-7d55-c9f6cdcfc5c8.gif)


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

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

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

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