AngularJS v2.0 Design Docs の中で、directive 周りについて書かれている Templating の内容をざっと読んでまとめてみました。Controller がなくなる 2.0 では最も気になる部分です。
2014/11/3 当時の内容であり、どんどん変わっていくと思われます。かなり内容をはしょってあるので悪しからず。
また ng-europe での発表と食い違ってるところもあり、どっちが新しいのか不明です。あくまでも、雰囲気を掴む程度に・・・。
まだの方は、まず ng-europe のビデオ を見ることをおすすめします。
まとめ
最初にまとめを。
- 1.x の自由で複雑すぎた directive を、3 つのパターンに分類。
- @ アノテーションでいろいろ設定。
- DI で他のディレクティブと通信できる。
- Web Components 的な方向に。まだしっかり詰まってなさそう。
1.x で controller, directive を書くときも、3 つのパターンを意識して書くと良さそうです。
概要
1.x の directive は複雑すぎた。2.0 では部品を減らし単純化する。
Angular ではカスタム要素を作るのではなく、DOM 要素に振る舞いを結びつける。この振る舞いが directive。JavaScript class で、マッチする要素ごとにインスタンス化される。
DOM 要素同様、directive もプロパティ(属性ではないのに注意。JavaScript で触れるやつ。)とイベントを使って作られる。
Directive の種類
1.x の経験により必要なパターンは以下の三つ。
- Decorative Directive
- Template Directive
- Component Directive
Decorator Directive
要素に機能を追加する。ツールチップをつけたり、要素の表示非表示を制御したり。ng-show
など。
Template Directive
DOM 要素をテンプレートにしてしまうもの。DOM 要素があった場所は、テンプレートのインスタンスが置ける hole(要素を置ける場所、のような意味?)になる。
ng-if
, ng-repeat
, ng-view
, ng-switch
, ng-include
など。
Execution context を作っても良い。ng-repeat
など。ng-if
などでは親 component の context をそのまま使えば良いので必要なし。
基本的なケースでは <template></template>
を使う。
<ul>
<template ng-repeat>
<li>...</li>
</template>
</ul>
直下の要素が一個の場合は以下でも良い。
<ul>
<li ng-repeat>...</li>
</ul>
<template></template>
で囲むことで、1.x にあった ng-repeat-start
, ng-repeat-end
のような directive は不要になる。
Component Directive
JavaScript のロジック、HTML テンプレート、(必須ではないけど)CSS をまとめたもの。
Execution context はアプリケーションの他の部分から独立している。テンプレート中でアクセスできるプロパティとメソッドは、対応する component directive のものだけ。
Shadow DOM を使うらしい。
Component のインターフェイスは以下。
- プロパティとイベント
- CSS プロパティ: Shadow DOM 的な方法で指定できる。
- 子要素: テンプレートで子 component の中に要素を入れることができる。それらの要素の execution context は子 component ではなく親 component のもの。(親のテンプレートに書くので普通に考えたらそうなる。)
Expressions
属性の値は全て expression。1.x では、ものによって違った.
ng-europe で発表されていたのは (click)="doSomething"
, [checked]="isSelected"
みたいなのだったけど、Design Doc では on-xxx=""
, bind-xxx=""
と書いてある。前者が新しい?
追記: Angular v2 Template Syntax Summary という Design Doc が前者の記法についてのもののようです。まだ書き途中の模様。
1.x では a.b.c
で a
や b
が undefined
でもエラーにならなかったが、2.0 ではエラーになる模様。
Directive API
アノテーション
@ アノテーションで ES6 class にメタ情報を付加。以下は directive の種類によらず共通な属性。
-
selector
: どの DOM 要素に適用するかの CSS セレクタ。input[type="text"]
,[ng-show]
とか。 -
events
: directive で使うイベント一覧。テンプレートでon-xxx=""
と使えるように。 -
visibility
: どのdirective
インスタンスからアクセス可能にするか。同じ要素、直接の子供、子孫全部から選べる。 -
microsyntax
:ng-repeat
のような特殊な記法を使いたい際に利用。
@TemplateDirective(
selector: '[ng-repeat]',
microsyntax: {
'ng-repeat': '$item-name in $collection [track by $track-by]'
}
)
class NgRepeat {
// ...
}
DI
コンストラクタに DI。Http など UI に関係ないものの他に、DOM 要素(HTMLElement)や他の directive を inject できる。
@ComponentDirective
class ChildComponent() {
@Inject(HTMLElement, ParentComponent)
constructor(element, parent) {
}
}
おそらく型アノテーションでもできるはず?
@ComponentDirective
class ChildComponent() {
constructor(element: HTMLElement, parent: ParentComponent) {
}
}
データバインディング
Directive の全てのプロパティはデータバインディングに利用可能。
@PropertySet
を使うと、setter の実行条件を細かく指定できる。参照が変わったか、コレクションの中身が変わったか、など。
フック
ライフサイクルフックもある。Directive クラスにあらかじめ決まった名前のメソッドを定義する。
attach
detach
TemplateDirective だけ templateLoaded
というのがある。
イベント
カスタムイベントを投げられる。Component に到達するまで DOM tree を登っていく。
@ng.DecoratorDirective(
selector: 'dialog',
events: ['close']
)
class Dialog {
@Inject(window.HTMLElement)
constructor(element) { ... }
close() {
var evt = new Event('close');
this.element.dispatchEvent(evt);
if (!evt.defaultPrevented()) {
// Really close the dialog...
}
}
}
多分 component の template で on-close="componentMethod()"
みたいな感じで使われるはず。
Web Components
Custom Elements API が利用可能な場合、component たちは、custom element として登録される。それで Polymer や X-Tag から使えるようになるとか。
Polymer とどう折り合いをつけるかは未定のよう。
Shadow DOM サポートしてないブラウザでも動くようにはする模様。