※1つ前の記事が第2章の途中なので順番前後しますが、業務でカスタムディレクティブの作成が必要になったので、第6章 ディレクティブを先に読みます。
ディレクティブとHTMLの構文規則
AngularJSでは、以下の記法でディレクティブを呼び出せる。
構文規則 | 記法 | 例 |
---|---|---|
(なし) | namespace-name | ng-repeat=”item in items” |
XML | namespace:name | ng:repeat=”item in items” |
HTML5 | data-namespace-name | data-ng-repeat=”item in items” |
XHTML | x-namespace-name | x-ng-repeat=”item in items” |
APIの概要
ディレクティブの設定項目
プロパティ名 | 目的 |
---|---|
restrict | テンプレートの中でこのディレクティブがどのように使われるか |
priority | 同じ要素で指定されているディレクティブの間で、実行の順序を決めるための数値 |
template | インラインのテンプレートを文字列として指定する |
templateUrl | テンプレートが置かれているURLを指定する |
replace | trueが指定されている場合、現在の要素が置き換えられる |
transclude | ディレクティブの子要素よして記述されているコンテンツが、新しいテンプレートの中に移される |
scope | 親の$scopeを継承せずに、このディレクティブのために新しい$scopeを生成する |
controller | ディレクティブ間での通信を可能にするためのコントローラを生成する |
require | このディレクティブが正しく機能するために必要な、他のディレクティブを指定する |
link | 生成されたDOMを変更したりイベントリスナーを追加したりデータバインディングを定義したりする |
compile | 繰り返されるディレクティブのテンプレートを変更する |
ディレクティブの名前
ディレクティブの名前は以下のように、モジュールのdirective()メソッドを使って指定する。
myModule.directive('directiveName', function factory(injectables));
ディレクティブの名前は自由に指定できるが、名前空間を指定して外部のディレクティブとの競合を防ぐのが一般的。
ディレクティブ定義のオブジェクト
restrictプロパティ
restrictプロパティを使うと、ディレクティブの形態を指定できる。値は複数指定可能(デフォルトはA)。
|値|形態|例|
|E|要素|
|A|属性|
|
|C|クラス|
<
div class=my-menu:Products>|
|M|コメント||
priorityプロパティ
1つの要素に複数のディレクティブが記述されており、これらの実行の順序を指定したい場合には、priorityプロパティを利用する。最も高い値が指定されているディレクティブが最初に実行される。指定がない場合のデフォルト値はゼロ。
なお、AngularJS自身のディレクティブ(ng-repeat等)にもpriorityが指定されており、例えばng-repeatのpriorityは1000。
templateプロパティ
要素のコンテンツを指定されたテンプレートで置き換える。
templateUrlを使うと、サーバ上のファイルをテンプレートとして読み込む。
templateプロパティを使ったディレクティブの定義例:
(function () {
'use strict';
var appModule = angular.module('app', []);
appModule.directive('hello', function () {
return {
restrict: 'E',
templateUrl: 'helloTemplate.html',
replace: true
};
});
})();
helloTemplate.htmlの中身は以下。
<div>こんにちは</div>
Chromeを使っている場合、同一生成元ポリシーというセキュリティ上の制約のため、テンプレートをfile://で読み込めない。Webサーバを立てるか、Chromeの設定を変更する必要がある。
※Macユーザなら、ファイルのあるディレクトリに移動して、「php -S localhost:8080」でWebサーバを立てることができる。ファイルには「http://localhost:8080」というURLでアクセス可能。
transcludeプロパティ
transcludeプロパティを使うと、コンテンツを置き換えたり挿入したりする代わりにテンプレートへとコンテンツを移動できる。
var appModule = angular.module('app', []);
appModule.directive('hello', function () {
return {
template: '<div>こんにちは<span ng-transclude></span></div>',
transclude: true
};
});
使い方は以下。
<div hello>ボブ</div>
表示は「こんにちはボブ」となる。
compileとlink
compileとlinkは、AngularJSのビュー生成時に実行される。
流れとしては、スクリプトの読み込み→compile→link。
compile: テンプレートを加工する
link: ビュー内のデータを加工する
compileは以下のような構造になっている。
compile: function compile (tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
}
linkは以下のようになる。
link: function postLink(scope, iElement, iAttrs) { ... }
linkは$scopeにアクセスできるが、compileはできない(compileの段階では、まだ$scopeは用意されていない)。DOMの構造を変更したい場合は、postLinkの中に処理を記述する。
scopeオブジェクト
ディレクティブの設定によって、ディレクティブに渡される$scopeオブジェクトの中身が変化する。
$scopeの種類 | コード |
---|---|
既存の$scope | scope: false(デフォルト) |
新しい$scope | scope: true |
隔離された$scope | scope : {/* 属性名と関連付けの方式 */} |
※新しい$scope: 包含するコントローラの$scopeを継承して生成された$scope
※隔離された$scope: 親からモデルのプロパティをまったく継承せずに生成される$scope
関連付けの方式には以下のものがある。
記号 | 意味 |
---|---|
@ | 属性値を文字列として渡す |
= | このプロパティとディレクティブにとっての親の$scopeとの間でデータバインディングを行う |
& | 後で呼び出すための関数を親の$scopeから渡す |
controllerプロパティ
入れ子状になったディレクティブが互いにやり取りを行いたい場合には、コントローラを利用する。
requireプロパティの使用方法
文字列 | 使い方 |
---|---|
directiveName | どのコントローラを利用するかをキャメルケースで指定する |
^directiveName | DOMの木構造の中で祖先の要素に対しても探索を行う |
?directiveName | 対象のコントローラが見つからなかった場合に例外を発生しない |
本書に載っているディレクティブの説明は以上。難しいと噂だったので気構えていたのですが、本書に載っている範囲だと、すごく難しいというほどでもないですね。設定項目は多いですが、リファレンスが手元にあれば対応できる範囲。
まあ、本書のレベルでは出てこない落とし穴が色々あるんでしょうが。。。