ディレクティブを作成すると、コンパイラ、リンク関数、またはコントローラにコードを入れることができます。
文書では、彼らは次のように説明しています。
しかし、私にとっては、どの種類のコードがどこに使用されるべきかが明確ではありません。
例:コンパイル時に関数を作成し、それらをリンク内のスコープにアタッチすることも、コントローラ内のスコープにのみアタッチすることもできますか?
各ディレクティブが独自のコントローラを持つことができる場合、コントローラはディレクティブ間でどのように共有されますか?コントローラは本当に共有されているのか、それとも単にスコープのプロパティなのか。
コンパイル:
これがAngularが実際に指令をコンパイルする段階です。このコンパイル関数は、与えられた指令を参照するたびに1回だけ呼び出されます。たとえば、ng-repeatディレクティブを使用しているとします。 ng-repeatは、それがアタッチされている要素を調べ、それがアタッチされているHTMLフラグメントを抽出し、テンプレート関数を作成する必要があります。
あなたがHandleBars、アンダースコアテンプレート、またはそれと同等のものを使ったことがあるなら、テンプレート関数を抽出するためにそれらのテンプレートをコンパイルするようなものです。このテンプレート関数にデータを渡し、その関数の戻り値は正しい場所にデータがあるHTMLです。
コンパイル段階はテンプレート関数を返すAngularのそのステップです。角度によるこのテンプレート関数は、リンク関数と呼ばれます。
リンクフェーズ:
リンクフェーズは、データ($ scope)をリンク関数に添付する場所で、リンクされたHTMLが返されます。このhtmlがどこに行くのか、何が変わるのかもディレクティブが指定しているので、すでに行っておくのはいいことです。これは、リンクされたHTML、つまりデータがすでに添付されているHTMLに変更を加える機能です。リンク機能でコードを書く場合、角度付きでは、一般にリンク後機能(デフォルト)です。リンク機能がデータをテンプレートにリンクした後に呼び出されるコールバックの一種です。
コントローラー:
コントローラは、ディレクティブ固有のロジックを入れる場所です。このロジックはリンク関数にも入る可能性がありますが、それを「共有可能」にするためにはそのロジックをスコープに含める必要があります。それに関する問題は、あなたがその時あなたのディレクティブのもので範囲を壊しているということです。それで、2つの指令が互いに話し合いたい、あるいは互いに協力したいのであれば、代替案は何でしょうか。もちろん、そのすべてのロジックをサービスに組み込んでから、両方のディレクティブをそのサービスに依存させることもできますが、それだけでもう1つの依存関係がもたらされます。別の方法としては、このスコープにControllerを提供し(通常はスコープを分離しますか?)、そのディレクティブが別のディレクティブを「必要とする」ときに、このコントローラーを別のディレクティブに注入します。例として、angularjs.orgの最初のページのタブとペインを見てください。
私はGoogleチームのO'Reily AngularJSの本が何を言っているのかも追加したいと思いました:
Controller - ディレクティブ間で通信するためのAPIを公開するコントローラを作成します。良い例は 指令から指令への通信
Link - 結果として得られるDOM要素インスタンスをプログラム的に修正し、イベントリスナーを追加し、そしてデータバインディングを設定する。
コンパイル - ng-repeatで使用されるときのように、ディレクティブのコピーにまたがる機能のためにDOMテンプレートをプログラム的に修正します。コンパイル関数は、結果の要素インスタンスを修正するためにリンク関数を返すこともできます。
directive
を使用すると、Webコンポーネントを構築するための宣言的な方法でHTMLの語彙を拡張できます。 ng-app
属性はディレクティブであるため、ng-controller
およびすべてのng- prefixed attributes
も同様です。ディレクティブはattributes
、tags
、さらにはclass
names
、comments
にすることができます。
ディレクティブの生まれ方(compilation
とinstantiation
)
コンパイル: レンダリングする前にDOMをcompile
するためにmanipulate
関数を使用し、link
関数を返します(これによってリンクが処理されます)。これは、このディレクティブのすべてのinstances
と共有する必要があるメソッドを置く場所でもあります。
link: link
関数を使用して、特定のDOM要素(テンプレートから複製されたもの)にすべてのリスナーを登録し、ページへのバインディングを設定します。
compile()
関数で設定された場合、それらは一度だけ設定されていたでしょう(これはあなたが望むことが多いです)。 link()
関数に設定すると、HTML要素がオブジェクト内のデータにバインドされるたびに設定されます。
<div ng-repeat="i in [0,1,2]">
<simple>
<div>Inner content</div>
</simple>
</div>
app.directive("simple", function(){
return {
restrict: "EA",
transclude:true,
template:"<div>{{label}}<div ng-transclude></div></div>",
compile: function(element, attributes){
return {
pre: function(scope, element, attributes, controller, transcludeFn){
},
post: function(scope, element, attributes, controller, transcludeFn){
}
}
},
controller: function($scope){
}
};
});
Compile
関数は、pre
およびpost
リンク関数を返します。リンク前の関数では、インスタンステンプレートとcontroller
からのスコープも持っていますが、それでもテンプレートはスコープにバインドされておらず、まだ内容が透明になっていません。
Post
link関数はpost linkが最後に実行される関数です。これでtransclusion
は完成し、the template is linked to a scope
、そしてview will update with data bound values after the next digest cycle
は完成しました。 link
オプションはpost-link
関数を設定するための単なるショートカットです。
controller: ディレクティブコントローラは他のディレクティブリンク/コンパイルフェーズに渡すことができます。指令間通信で使用するための手段として、他のディレクトリに注入することができます。
必要なディレクティブの名前を指定する必要があります - それは同じ要素またはその親にバインドされるべきです。名前の前には次のものを付けることができます。
? – Will not raise any error if a mentioned directive does not exist.
^ – Will look for the directive on parent elements, if not available on the same element.
複数のディレクティブコントローラが必要な場合は、角括弧[‘directive1′, ‘directive2′, ‘directive3′]
を使用してください。
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope, $element) {
});
app.directive('parentDirective', function() {
return {
restrict: 'E',
template: '<child-directive></child-directive>',
controller: function($scope, $element){
this.variable = "Hi Vinothbabu"
}
}
});
app.directive('childDirective', function() {
return {
restrict: 'E',
template: '<h1>I am child</h1>',
replace: true,
require: '^parentDirective',
link: function($scope, $element, attr, parentDirectCtrl){
//you now have access to parentDirectCtrl.variable
}
}
});
また、コントローラー対リンク関数を使用する良い理由は(両方ともスコープ、エレメント、およびattrsにアクセスできるため)、使用可能なサービスまたは依存関係を(任意の順序で)コントローラーに渡すことができるからです。あなたはリンク機能でそれを行うことはできません。異なる署名に注意してください。
controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...
vs.
link: function(scope, element, attrs) {... //no services allowed
これはディレクティブフェーズを理解するのに良いサンプルです http://codepen.io/anon/pen/oXMdBQ?editors=101
var app = angular.module('myapp', [])
app.directive('slngStylePrelink', function() {
return {
scope: {
drctvName: '@'
},
controller: function($scope) {
console.log('controller for ', $scope.drctvName);
},
compile: function(element, attr) {
console.log("compile for ", attr.name)
return {
post: function($scope, element, attr) {
console.log('post link for ', attr.name)
},
pre: function($scope, element, attr) {
$scope.element = element;
console.log('pre link for ', attr.name)
// from angular.js 1.4.1
function ngStyleWatchAction(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) {
element.css(style, '');
});
}
if (newStyles) element.css(newStyles);
}
$scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);
// Run immediately, because the watcher's first run is async
ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
}
};
}
};
});
html
<body ng-app="myapp">
<div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
<div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
</div>
</div>
</body>