AngularJSアプリケーション開発ガイド 第6章 ディレクティブ

AngularJSアプリケーション開発ガイド

※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 対象のコントローラが見つからなかった場合に例外を発生しない

本書に載っているディレクティブの説明は以上。難しいと噂だったので気構えていたのですが、本書に載っている範囲だと、すごく難しいというほどでもないですね。設定項目は多いですが、リファレンスが手元にあれば対応できる範囲。

まあ、本書のレベルでは出てこない落とし穴が色々あるんでしょうが。。。

コメントをどうぞ

コメントを残す