第3回ではコンポーネントについて解説しました。今回はデコレーターについて詳しく説明します
今回の元記事は4. Creating a Custom Decoratorです。
今回は次のようなツールチップ用の独自属性を定義します。
<span tooltip="tooltipForRecipe(recipe)">
...
</span>
まずはサンプルアプリを動かしてみよう
こちらにサンプルアプリのソースコードがあります。リポジトリをクローンして、Chapter_04ディレクトリで> pub serve
すればサンプルアプリが起動します。
第3回と同様にレシピ一覧とその詳細が表示されます。また、一覧のアイテムにマウスを乗せるとツールチップが表示されます。
デコレーターの作成
ツールチップ用のデコレーターの作成は次の手順で行います。
- 2つのクラスの追加 : Tooltip(デコレーターの実装クラス)とTooltipModel(データを持つモデル)
- RecipeBookComponentの変更 : レシピからツールチップのモデルを作成
- web/main.dartでTooltipの登録
- ツールチップのデコレーターを使う
デコレーターの定義
デコレーターは次のように定義します。
...
import 'package:angular/angular.dart';
@Decorator(selector: '[tooltip]')
class Tooltip {
final dom.Element element;
@NgOneWay('tooltip')
TooltipModel displayModel;
...
Tooltip(this.element) {
...
}
...
}
前回も説明したとおり、selector
にはコンポーネントを表すCSSセレクタを指定します。上の例では[tooltip]
はDOM属性として定義されています。また、tooltip
属性(この場合自分自身)の値はdisplayModel
に対してHTMLからDartへの一方通行のバインディングを定義しています。このデコレーターは次のように呼び出すことになります。
<span tooltip="tooltipForRecipe(recipe)">
デコレーターのコンストラクタは、そのデコレーターが適用されたDOM要素が表示された際に呼ばれ、コンストラクタの引数としてElement
型のインスタンスが与えられます。
デコレーターによるDOMの操作
ツールチップの実装の中で最も重要なのは<div>
要素を作成し、DOMに追加している部分です。マウスオーバーのイベントが発生するとコールバックでこの処理が実行されます。
import 'dart:html' as dom;
...
// In an onMouseEnter handler:
tooltipElem = new dom.DivElement();
// ...Create children using info from displayModel...
// ...Add the children to the <div>...
// ...Style the <div>...
dom.document.body.append(tooltipElem);
重要 デコレーターでDOMを操作する際は次のことに気をつけましょう
- DOMの操作はコンストラクタ 以外で行う
- Angularが管理している要素は破棄しない
AngularはDOMの全体を管理しているので、コードで直接DOMを操作する際は十分注意しましょう
ツールチップのデータモデルは次のようになっています
class TooltipModel {
String imgUrl;
String text;
int imgWidth;
TooltipModel(this.imgUrl, this.text, this.imgWidth);
}
ツールチップのモデルを生成する
recipe-bookコンポーネントでレシピのデータからツールチップのモデルを生成します。
class RecipeBookComponent {
...
static final tooltip = new Expando<TooltipModel>();
TooltipModel tooltipForRecipe(Recipe recipe) {
if (tooltip[recipe] == null) {
tooltip[recipe] = new TooltipModel(recipe.imgUrl,
"I don't have a picture of these recipes, "
"so here's one of my cat instead!",
80);
}
return tooltip[recipe]; // recipe.tooltip
}
ここでExpandoを使っているのは実装上の工夫です。Expando
はすでに存在するオブジェクトに新たなプロパティを関連付ける方法の一つです(この場合はRecipe
にTooltipModel
を関連付ける)。
コンポーネントとデコレーター
デコレーターは属性としてのコンポーネントと似ていますが、デコレーターにはスコープが無いという点が大きく違います。デコレーターは単にHTML要素に新しい機能を追加するだけの修飾でしかありません。
まとめ
- デコレーターは既存のHTML要素を拡張する
- デコレーターはスコープを持たない
- DOMを直接操作する際は注意する
第5回は5. Introducing Formatters and Services
を元にバージョン1.0に置き換えて解説します。