Dartでひと足お先に「Web Components」体験(実装編)

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

毎度お世話になっております。クラスメソッドの稲毛です。

前回、Dart Editor上でのWeb Components用環境(プロジェクト)構築に続いて、今回はWeb Componentsを利用する実装編となります。

Web Componentの構築

Web Componentは「element」要素内に記述します。今回は「++」と「--」というアンカーを持ち、クリックすることで数値が加算、減算されるカウンタコンポーネントを作成してみます。

    <element name="x-counter" constructor="Counter" extends="div">
      <template>
        <h1>{{count}}</h1>
        <a href="#" data-action="click:increase">++</a>
        <a href="#" data-action="click:decrease">--</a>
      </template>
      <script type="application/dart">
        import 'package:web_components/web_component.dart';
    
        class Counter extends WebComponent {
          int count = 0;
          increase(event) => count++;
          decrease(event) => count--;
        }
      </script>
    </element>

element要素

「element」要素にはそのコンポーネントの名称(name)、コンストラクタ名(constructor)、拡張元となる要素名(extends)を指定します。

    <element name="x-counter" constructor="Counter" extends="div">

ここでは、「x-counter」という名称で「Counter」というコンストラクタを持つ「div」要素を拡張するコンポーネントということになります。

script要素

「element」要素内の「script」要素にはコンポーネントのロジックを実装しますが、「web_components」パッケージが提供する「WebComponent」クラスを継承したクラスを定義し、その中にロジックを記述します。

      <script type="application/dart">
        import 'package:web_components/web_component.dart';
    
        class Counter extends WebComponent {
          int count = 0;
          increase(event) => count++;
          decrease(event) => count--;
        }
      </script>
8行目
「WebComponent」クラスを利用する為にライブラリ「web_component.dart」をインポートています。
10行目
「element」要素でコンストラクタ名を「Counter」と指定していますので、「WebComponent」クラスを継承したクラスの名前は「Counter」となります。
11行目
カウンタの現在値を表すインスタンス変数です。
12行目
インスタンス変数「count」をインクリメントするインスタンスメソッドです。
13行目
インスタンス変数「count」をデクリメントするインスタンスメソッドです。

template要素

「element」要素の「template」要素に表示要素を記述します。

    <element name="x-counter" constructor="Counter" extends="div">
      <template>
        <h1>{{count}}</h1>
        <a href="#" data-action="click:increase">++</a>
        <a href="#" data-action="click:decrease">--</a>
      </template>
    </element>

「template」要素では、その名の通りテンプレート機能を利用して表示要素を定義します。テンプレートのシンタックスには、JavaScriptフレームワークの「AngularJS」などと同様に「MDV(Model-driven Views)」によるシンタックスを使用します。

        <h1>{{count}}</h1>
        <a href="#" data-action="click:increase">++</a>
        <a href="#" data-action="click:decrease">--</a>
3行目
「{{…}}」の記述を用いて「Counter」クラスに用意したインスタンス変数「count」とバインディングしています。インスタンス変数「count」が変更された場合、この「h1」要素の値も更新されます。
4行目
「data-action」属性に「MDV Template」が提供する簡潔な記法でクリックイベントのハンドラを指定しています。「click:increase」で「clickイベントが送出されたらincreaseメソッドを処理する」ということを表しています。
5行目
4行目と同じくクリックイベントのハンドラを指定しています。「click:decrease」で「clickイベントが送出されたらdecreaseメソッドを処理する」ということを表しています。

コンポーネントの利用

「element」要素の内容は表示上のDOMツリーに含まれない為、表示されません。「element」要素に定義したコンポーネントを利用(表示)するには

<div is="x-counter"></div>

または

<x-counter></x-counter>

と指定します。「x-counter」は「element」要素の「name」属性に指定したコンポーネント名称ですね。

app.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <element name="x-counter" constructor="Counter" extends="div">
      <template>
        <h1>{{count}}</h1>
        <a href="#" data-action="click:increase">++</a>
        <a href="#" data-action="click:decrease">--</a>
      </template>
      <script type="application/dart">
        import 'package:web_components/web_component.dart';
    
        class Counter extends WebComponent {
          int count = 0;
          increase(event) => count++;
          decrease(event) => count--;
        }
      </script>
    </element>
    <x-counter></x-counter>
    <script type="application/dart">
      main(){}
    </script>
  </body>
</html>

アプリケーションの実行

今回は「build.dart」ファイルを利用してコンパイルする方法を取っているので「out」フォルダ内にコンパイル後のソースが出力されます。

Webアプリケーション「app.html」をコンパイルしたものは「_app.html.html」となります。このファイルをコンテキストメニューの「Run in Dartium」等で実行します。

このようにコンポーネントが表示され、「++」および「--」のアンカーをクリックすることでカウント値が変化することが見て取れる筈です。

もちろん次のように複数のコンポーネントを指定すれば

    <x-counter></x-counter>
    <x-counter></x-counter>
    <x-counter></x-counter>

複数コンポーネントが表示され、カウント値も個別にカウントされます。

「data-value」属性を用いれば、コンポーネントに初期値を渡すことも可能です。

    <x-counter></x-counter>
    <x-counter data-value="count: 5"></x-counter>
    <x-counter data-value="count: 10"></x-counter>

コンポーネントのインポート

ここまででコンポーネントを作成し実際に使用することが出来ましたが、コンポーネントがメインのHTMLに埋め込まれていては再利用性の面でムムムッな感じですよね。Dart Web Componentsでは外部で定義したコンポーネントをインポートすることも可能です。

コンポーネントファイルの作成

コンポーネントファイルは「element」要素を持ったHTMLファイルとして用意します。

counter.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <element name="x-counter" constructor="Counter" extends="div">
      <template>
        <h1>{{count}}</h1>
        <a href="#" data-action="click:increase">++</a>
        <a href="#" data-action="click:decrease">--</a>
      </template>
      <script type="application/dart">
        import 'package:web_components/web_component.dart';

        class Counter extends WebComponent {
          int count = 0;
          increase(event) => count++;
          decrease(event) => count--;
        }
      </script>
    </element>
  </body>
</html>

コンポーネントのインポート

コンポーネントのインポートにはCSSファイルなどと同様に「link」要素を用います。

app.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <link rel="components" href="counter.html">
  </head>
  <body>
    <x-counter></x-counter>
    <script type="application/dart">
      main(){}
    </script>
  </body>
</html>

Counterコンポーネントが「counter.html」として使いまわせるようになりました!(^^)

まとめ

如何でしたでしょうか?
今回は一度コンパイルする形式を取りましたが、将来的にブラウザによってオンデマンドにコンパイルされるようになればもっと身近なものになると思います。Dartだけの話ではなく「Web Components」が正式に採用されたら、もっとWebアプリケーション開発が楽しくなりそうですね!(^^)

参照