Nagoya.dart #1 - わかる!Web Components -

published date : 2013-09-21

参加者募集ページ

Nagoya.dartとは

構造化Webプログラミング言語Dart及びそのエコシステムについて、 学習したりOSSに貢献したりといった活動を名古屋を中心に行なっていきます。


第1回でやること

  1. Web Componentsの概要
  2. Polymerの概要
  3. Polymer.dartプロジェクト作成
  4. Model Driven View
  5. Data binding
  6. observe
  7. Custom Elements
  8. TodoMVC

事前準備

DartEditorをインストールしておきます。

こちらからダウンロード後、展開するだけでOK。

ちなみにWindows XPは未サポートなので、さっさと捨てましょう。

1. Web Componentsの概要

Web Componentsとは、保守性・再利用性・開発効率を目的に、WebのUIをコンポーネント化したもの。GoogleによってW3Cに仕様が提案された。

W3C 草案

以下のスライドがわかりやすい。
Web Component概要

従来の(クライアント)サイドテンプレートエンジンやUIコンポーネントを標準化した感じ。

2. Polymerの概要

Web Componentsは現在、Chromeが一部サポートしているのみで、他のブラウザでは使えない。

が、GoolgeがGoogle IO 2013で発表したPolymer.jsによって、Web Componentsの仕様のほとんどを動作させることができるようになった。

ブラウザ対応状況

Polymer.dartはPolymer.jsのDart版で、コンポーネントをDartで作ってdart2jsでJavaScript化してPolymer.jsを使用したアプリで使う、ということができるようになった。

アーキテクチャ概要

Polymer Architecture

3.Polymer.dartプロジェクト作成

  1. File > New Application2.
  2. Web Application (using the polymer library)を選択
  3. たぶんエラー出る
  4. Tool > Pub Install
  5. build.dartを選択して、Don’t Run build.dart(うざいので)
  6. プロジェクト名.htmlを開いてCommand + rでDartium上で動作することを確認

※ 現在プロジェクトテンプレートのbuild.dartがまともに動かないので、実質まともに使えな(ry

4.Model Driven View

DartでTemplatingといったらコレ。
(以降のソースコードはDart AdvocateのSeth LaddのGitHubリポジトリから拝借しました)

web/bindtosimple_project/index.html
<!DOCTYPE html>

<html>
  <head>
    <title>index</title>
    <script src="packages/polymer/boot.js"></script>
  </head>

  <body>   
    <!-- {{"{{msg"}}}} says to bind to the msg field of the currently bound object -->
    <template id="tmpl" bind>
      <p>Hello {{"{{msg"}}}}</p>
    </template>

    <script type="application/dart" src="index.dart"></script>
  </body>
</html>
web/bindtosimple_project/index.dart
import 'dart:html';

class Message {
  String msg;
}

main() {
  var message = new Message()..msg = 'world';
  query('#tmpl').model = message;
}

5.Data binding

web/bindtoinputtextfield/index.html
<!DOCTYPE html>

<html>
  <head>
    <title>index</title>
    <script src="packages/polymer/boot.js"></script>
  </head>

  <body>
    <template id="tmpl" bind>
      <div>
        Type something: <input type="text" value='{{"{{message"}}}}'>
      </div>
      <div>
        You typed {{"{{message"}}}}
      </div>
    </template>

    <script type="application/dart" src="index.dart"></script>
  </body>
</html>

web/bindtoinputtextfield/dart
import 'dart:html';
import 'package:polymer/polymer.dart';

class App extends Object with ObservableMixin {
  @observable
  String message;
}

main() {
  query('#tmpl').model = new App();
}

6.observe

Polymer.dartに依存してるわけではないけど、利用するシーンは多そうなので。

web/observable_getter/index.html
<!DOCTYPE html>

<html>
  <head>
    <title>index</title>
    <script src="packages/polymer/boot.js"></script>
  </head>

  <body>
    <template id="tmpl" bind>
      <div>
        The timestamp is {{"{{timestamp"}}}}
      </div>
      <div>
        The current second is {{"{{second"}}}}
      </div>
    </template>

    <script type="application/dart" src="index.dart"></script>
  </body>
</html>
web/observable_getter/index.dart
import 'dart:html';
import 'dart:async';
import 'package:polymer/polymer.dart';

class App extends Object with ObservableMixin {
  @observable
  DateTime timestamp;

  App() {
    bindProperty(this, const Symbol('timestamp'),
        () => notifyProperty(this, const Symbol('second')));
  }

  int get second => timestamp.second;
}

main() {
  App app = new App();
  new Timer.periodic(const Duration(seconds: 1), (_) {
    app.timestamp = new DateTime.now();
  });
  query('#tmpl').model = app;
}

7.Custom Elements

実際にCustom Elementを定義してみる。

web/customelement/myelement.html
<!DOCTYPE html>

<!-- Use polymer-element instead of element -->
<polymer-element name="my-element">
  <template>
    <p>Hello from inside of a custom element!</p>
    <p>The counter is {{"{{counter"}}}}</p>
  </template>

  <script type="application/dart" src="my_element.dart"></script>
</polymer-element>
web/customelement/myelement.dart
library my_element;

import 'package:polymer/polymer.dart';

// Custom elements extend PolymerElement.
// Apparently, you also have to use an annotation to mark what tag this
// belongs to.
@CustomTag("my-element")
class MyElement extends PolymerElement with ObservableMixin {
  // custom functionality goes here

  @observable int counter = 0;

  void increment() {
    counter++;
  }
}

使う側。Custom Elementにバインドされているオブジェクトのメソッドを使える。

web/custom_element/index.html
<!DOCTYPE html>

<html>
  <head>
    <title>index</title>
    <!-- you have to import the HTML file that contains the custom element -->
    <link rel="import" href="my_element.html">

    <!-- This is the bootstrap script for polymer. Use this INSTEAD of
         dart.js -->
    <script src="packages/polymer/boot.js"></script>
  </head>

  <body>

    <!-- you must close custom elements -->
    <my-element id="main"></my-element>

    <script type="application/dart" src="index.dart"></script>
  </body>
</html>
web/custom_element/index.dart
import 'dart:html';
import 'dart:async';
import 'my_element.dart';

main() {
  Element elem = query('#main');
  MyElement myElement = elem.xtag; // Get to the custom element via xtag

  new Timer.periodic(const Duration(seconds: 1), (t) {
    myElement.increment();
  });
}

8.todoMVC

Tools > Welcome Page > TodoMVC

すごくわかりづらいので、リファクタリングしてみましょう!