2012/12/27更新:この記事は一部情報が古いので、こちらと併せて読むといいかも。
Dart Web Componentsの記事が10月にアップされて、それからしばらくはバグってたりでまともに使えなかったんだけど、今日試したらなんとか使えた。で、Web Componentsマジさいこーってなったのでちょっとここで参考用に情報を残しておこうと思う。試した環境はUbuntu Linux 12.10(64 bit)。
手順
注:最近はDartで互換性を捨てる変更がいろいろと入ってるため、タイミングによっては同じやり方でうまく行かないかもしれない。
Web Componentsを試すにはいろいろと手順が必要で、順番的には以下の通りになる。
- Dart SDK、Dartiumの準備
- テスト用のパッケージの生成
- htmlの記述、およびdwc.dartによるコンパイル
- Dartiumでの閲覧
それぞれ順を追って説明する。
Dart SDK、Dartiumの準備
まずすべきは最新のDartiumとSDKを落としてくること。Editorごと落とすと全部含まれているのでそれが楽かも。場所はここ(64bit版)。なぜ最新版でないといけないかというと、安定版では最新のDartの変更が取り込まれていないため、あとの手順で失敗してしまうから。(2012/11/4現在)
Editorを落としたら解凍してでてきたdartディレクトリにパスを通すんだけど、自分は~/binの下にdartディレクトリをおいて、~/.bashrcに以下のように書いてる。
export PATH=$PATH:~/bin/dart/dart-sdk/bin
これで必要な実行ファイル、特にdartとpubにパスが通る。
テスト用パッケージの生成
Dartはコードのパッケージ化を推進していて、今回使うWeb Componentsももちろんパッケージ化されている。パッケージ化されたコードを使う側もパッケージ化したほうが何かと都合がいいので、その作法に従うことにする。パッケージ化の詳細な手順は公式なドキュメントに任せるとして、ここでは最低限必要な部分だけを説明する。
- パッケージのルートディレクトリの生成
パッケージ関連のファイルを格納する適当なディレクトリを作る。今回はdart-web-components-testとする。
- pubspec.yamlの作成
pubspec.yamlというファイルはパッケージの情報、例えば名前やら依存する他のパッケージやらの情報を記述するファイルで、Web Componentsのパッケージを持ってくるためにこれを記述しなければならない。といっても内容は単純で、以下のとおり。
name: dart-web-components-test dependencies: web_components: any
パッケージの名前と、あとweb_componentsというパッケージに依存してることを示している。内容はこれだけ。
- pubによる依存パッケージのダウンロード
今度は先ほど作ったpubspec.yamlを使って依存するパッケージをダウンロードする。dart-web-components-testのしたにpubspec.yamlを置いて、以下のコマンドをうつ。
$ pub install Resolving dependencies... Downloading web_components 0.2.5+2 from hosted... Downloading js 0.0.7 from hosted... Downloading html5lib 0.0.19 from hosted... Dependencies installed! $
これでWeb Componentsを使うのに必要なパッケージがダウンロードされて、自動的に作られたpackagesディレクトリの下に置かれる。ラクチン。ちなみにpackagesの中身は下のようになってるはず。
$ ls packages/ args html5lib js logging unittest web_components
htmlの記述、およびdwc.dartによるコンパイル
必要なパッケージがそろったので、今度は実際にコードを書いていく。Web Componentsなコードの書き方はこの記事に詳しく書いてあるのでそっちを見るのをおすすめする。MVVM(Model-View-View Model)なコードに経験がある人ならあっさり理解できると思うけど、そうでない人にはもしかしたらちょっとわかりづらいかも。幸い俺は経験があるので問題なかった。
index.html
以下にサンプルのコードを示す。内容は動物のリストを表示するという単純なもの。中ではWeb Componentsの要素(element, template, iterate等)を色々と使ってる。
<!doctype html> <html> <head> <title>Dart Web Components</title> <link rel="stylesheet" type="text/css" href="dart-web-components-test/bootstrap.min.css"> <script src="http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script> </head> <body> <!-- Summary of Animal --> <element name="animal-summary" constructor="AnimalSummaryViewModel" extends="div"> <template> <div>{{header}}</div> <button data-action="click:increment">Click to increment</button> <input type="text" data-bind="value:name" placeholder="type name here"> </template> <script type="application/dart"> import 'package:web_components/web_component.dart'; /// Model class of animal class AnimalSummary { AnimalSummary(this.name, this.count); String name = "No Name"; int count = 0; } /// View Model class of animal class AnimalSummaryViewModel extends WebComponent { /// read-only header String get header => "${_animal.name}:${_animal.count}"; void increment(e) { _animal.count++; } String get name => _animal.name; String set name(String s) => _animal.name = s; /// accessor for the underlying model AnimalSummary get model => _animal; AnimalSummary set model(AnimalSummary ts) => _animal = ts; AnimalSummary _animal; } </script> </element> <div id="navigationArea" class="navbar"> <div class="navbar-inner"> <div class="container"> <a class="brand" href="#">Dart Web Components</a> </div> </div> </div> <div id="animalListArea"> <ul> <template iterate='a in animalList'> <animal-summary data-value="model: a"/> </template> </ul> </div> <script type="application/dart"> // create a list of animals to show List<AnimalSummary> animalList = <AnimalSummary>[ new AnimalSummary("aardvark",0), new AnimalSummary("giraffe",1) ]; void main() {} </script> </body> </html>
コードが書けたら今度はそれを一旦コンパイルしないといけない。これは最終的には不要になる(と期待している)けど、現状は必要みたい。ということで以下のコマンドをうつ。
$ dart --package-root=packages/ packages/web_components/dwc.dart index.html Total time spent on index.html -- 270 ms $ ls _index.html.animal_summary.dart _index.html_bootstrap.dart packages _index.html.dart bootstrap.min.css pubspec.lock _index.html.html index.html pubspec.yaml
これでindex.htmlの解析がおこなわれて、無数のよくわからないファイル(_index.html*)が生成される。
Dartiumでの閲覧
これで必要なファイルが揃ったのでいよいよDartiumを起動して結果を確認する。Dartiumを起動するときは下のコマンドを使用する。
$ ~/bin/dart/chromium/chrome --user-data-dir --enable-experimental-webkit-features --allow-file-access-from-files &
これでWeb Componentsが有効化されたDartiumが起動する。ついでにローカルファイルアクセスを有効化するフラグもつけた。この状態で先ほど生成された_index.html.htmlを開くと下のようになる。
テキストボックス上で名前をいじれば表示されている名前も変わるし、ボタンを押せば数字も増える。
AnimalSummaryViewModelのプロパティと各種のView、例えば名前とテキストボックスがバインド(Web Component的には別の用語かも)されていて、名前が変更された場合は自動的にその変更が反映されるようになっている。
またulの中のli要素も"iterate='a in animalList'"という書き方でリストから自動的に生成させることができる。その生成される要素もelementとして定義したanimal-summary要素で、すごいすっきりと宣言的に書くことができている。
まとめ
ということでDart Web Componentsを試してみた。JavaScriptではBackbone.jsとかで似たようなことはできるっぽいけど、やっぱり公式にサポートしてもらえると大変助かる。あと最近はC# + WPF + MVVMを書くことが多かったので、Webでも似たような書き方ができるというのは非常にありがたい(というかそうでないとめんどくさすぎてヤル気が起きないくらい)。今後もWeb Componentsには頑張ってほしい。