コントローラとテンプレートを組み合わせたディレクティブを作成する
3つめのサンプルは、HTMLテンプレートとコントローラをセットにしたカスタムディレクティブの作成です。この組み合わせでカスタムディレクティブが作成できるようになれば、部品化したディレクティブとして管理ができるようになります。
今回作成するサンプルは、図3のようにテキストフィールドに入力した文字を利用して、「こんにちは、XXさん」のように表示します。
最初に表示される名前は、属性値を利用して指定できることと、実際のHTMLは別のテンプレートで管理します。従って、実際に利用する場合には、リスト5のように非常にシンプルなテンプレートになります。
<!DOCTYPE html>
<html lang="ja" ng-app="template">
<head>
<meta charset="UTF-8">
<title>My Template Directive</title>
<script type="text/javascript" src="../vendors/angular.js"></script>
<script type="text/javascript" src="template.js"></script>
</head>
<body>
<my-template default="ゲスト" /> <!-- (1)myTemplateディレクティブの利用 -->
</body>
</html>
今回、作成したディレクティブの名称はmyTemplateのために(1)のように"my-template"タグとして指定します。実際にこのタグ内に表示するためのテンプレートはリスト6のようになります。また、前回までは属性として指定していましたが、デフォルトでは属性で指定することも、要素名でも指定することもできますが、今回は要素名でのみ指定可能としますので、この指定もディレクティブの作成時に行います。
<div>
<div>あなたの名前をいれてください</div>
<input type="text" ng-model="name" />
<hr />
<h1 class="message" ng-show="showMessage">こんにちは「{{ name }}」さん</h1>
</div>
myTemplateディレクティブを実装したコードがリスト7のようになります。
(function(){
'use strict';
angular.module('template',[])
.directive('myTemplate',[templateDirective]);
function templateDirective(){
// (1)ディレクティブの作成に必要なオブジェクトを返す
return {
controller : ['$scope',templateController], //(2)(6)で定義したコントローラを指定する
scope: {
default : '@' // (3)default属性の値をそのまま利用する
},
restrict: 'E', // (4)要素名(E)のみを指定する
templateUrl : 'template.html', // (5)テンプレートファイル名を指定する
link : function(scope,element){
scope.showMessage = false;
scope.$watch('name',function(val){
if(val){
scope.showMessage = true;
}
else{
scope.showMessage = false;
}
});
}
}
}
// (6)利用するコントローラを定義する
function templateController($scope){
// (7)default属性の値は$scope変数として利用出来る
$scope.name = $scope.default;
}
}());
(1)ではディレクティブの作成に必要なプロパティを指定したオブジェクトを返します。このプロパティはディレクティブ作成時に指定できるプロパティのように非常にたくさんありますが、今回は、controller、restrict、scope、templateUrl、linkプロパティを使用します。今までの例では、関数のみを定義していましたが、この場合にはlinkプロパティのみを指定していることと同じになります。
それぞれのプロパティを一つずつ説明していきます。
まず、(2)のcontrollerプロパティは、実際にmyTemplateで利用するコントローラを指定します。実際のコントローラの定義は(6)で行っています。今までの通常のコントローラの作成方法に慣れていれば、同様に行えるので直感的に理解が可能かと思います。
続いて、scopeです。このscopeプロパティが最も理解が難しいプロパティで、以下のような指定ができます。
| 指定できる値 | 説明 |
|---|---|
| false | 現在のscopeオブジェクトをそのまま利用する。(デフォルト) |
| true | 親のscopeオブジェクトを継承した新しいscopeオブジェクトを作成する。 |
| {...} | 独立したscopeオブジェクトを作成する。 |
falseの場合には、一般的にそのディレクティブが使われている上位タグのコントローラのscopeオブジェクトと同じオブジェクトになります。trueの場合は、親scopeオブジェクトを継承した形で新たなscopeオブジェクトが作成されます。ngControllerディレクティブがtrueを指定しているよいサンプルです。
そして、オブジェクト形式として指定した場合には、まったく独立したscopeオブジェクトが作成されます。部品として定義したい場合には、独立性を高くしたいのでこのような指定が通常になります。また、(3)ではdefaultの値として「@」のように記号の値を指定しています。これは、テンプレートでdefault属性として指定した値をそのまま、値としてscopeオブジェクトを作成するように指定です。従って、(7)では$scope.defaultという値はテンプレートで指定した「ゲスト」という文字列が設定されます。また、「@」以外の記号も利用でき、表3のように他の記号も利用可能です。
| 記号 | 説明 |
|---|---|
| '@' | 指定した値を文字列として取得できる。ただし、{{Angular式}}のように指定すれば、Angular式として評価後の値を取得できる。 |
| '=' | 指定した変数を双方向バインディング可能な変数として利用可能である。また、指定した値はAngular式として評価して値を取得できる。 |
| '&' | 指定した文字列をAngular式のFunctionとして取得できる。 |
'@default'のように記号+属性名でも指定が可能です。その場合、scopeオブジェクトの変数名とディレクティブの属性名を別に管理ができます。指定しなかった場合には、変数名=属性名として扱われます。ただし、属性名はディレクティブ名と同様にcamelCaseとして記述します。
続いて、(4)のrestrictは、作成したディレクティブを属性名や、要素名、クラス名として指定ができるもので、以下の指定が行えます。
| タイプ | 説明 |
|---|---|
| 'E' | 要素(Element)名として指定ができる。 |
| 'A' | 属性(Attribute)名として指定ができる。 |
| 'C' | クラス(Class)名として指定ができる。 |
| 'M' | コメントとして指定がでできる。 |
また、デフォルトは'EA'のように指定したものと同じです。
そして、最後が(5)のtemplateUrlは、テンプレートURLを指定します。テンプレートの内容は、リスト6のように、通常のAngularJSで利用するテンプレートのフォーマットが利用できますので、ngIncludeで管理していたテンプレートの移行が容易にできます。ただし、相対パスでの指定の場合には実行しているHTMLからの相対パスになるので注意してください。
また、関数も指定でき、リスト8のように動的にテンプレートURLを変えることもできます。この際に引数のelementとattrsはlink関数の引数で使用したものと同じです。
templateUrl : function(element,attrs){
return 'template_' + attrs['lang'] + '.html';
}
最後に
簡単なカスタムディレクティブから、少々複雑なカスタムディレクティブまでの作成方法を紹介しました。ここまで出来るようになれば、かなりのディレクティブが自分で作成できるようになると思います。
各プロパティなどの詳細な記述方法や説明は割愛させていただきましたが、あとはドキュメントを見ながら足りない部分を補っていけると思います。ぜひ、カスタムディレクティブを作成して、独立性や再利用性が高いプログラミングに挑戦してみてください。
