Angularディレクティブを記述する場合、次の関数のいずれかを使用して、DOMの動作、コンテンツ、およびディレクティブが宣言されている要素の外観を操作できます。
- コンパイル
- コントローラ
- プレリンク
- ポストリンク
どの関数を使用するべきかについては、いくつかの混乱があるようです。この質問の対象は次のとおりです。
Angularディレクティブを記述する場合、次の関数のいずれかを使用して、DOMの動作、コンテンツ、およびディレクティブが宣言されている要素の外観を操作できます。
どの関数を使用するべきかについては、いくつかの混乱があるようです。この質問の対象は次のとおりです。
回答:
次のplunkに基づいて、次のHTMLマークアップを検討してください。
<body>
<div log='some-div'></div>
</body>
次のディレクティブ宣言で:
myApp.directive('log', function() {
return {
controller: function( $scope, $element, $attrs, $transclude ) {
console.log( $attrs.log + ' (controller)' );
},
compile: function compile( tElement, tAttributes ) {
console.log( tAttributes.log + ' (compile)' );
return {
pre: function preLink( scope, element, attributes ) {
console.log( attributes.log + ' (pre-link)' );
},
post: function postLink( scope, element, attributes ) {
console.log( attributes.log + ' (post-link)' );
}
};
}
};
});
コンソール出力は次のようになります。
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)
私たちは、それを見ることができるcompile
最初の、そして実行されcontroller
、その後pre-link
、最後ですpost-link
。
注:以下は、リンク関数で子をレンダリングするディレクティブには適用されません。かなりの数のAngularディレクティブがこれを行います(ngIf、ngRepeat、またはを使用したディレクティブなど
transclude
)。これらのディレクティブは、子ディレクティブが呼び出さlink
れる前にネイティブに呼び出される関数を持ちcompile
ます。
多くの場合、元のHTMLマークアップはネストされた要素で構成され、それぞれに独自のディレクティブがあります。次のマークアップのように(plunkを参照):
<body>
<div log='parent'>
<div log='..first-child'></div>
<div log='..second-child'></div>
</div>
</body>
コンソール出力は次のようになります。
// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)
// The link phase
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)
ここでは、コンパイルフェーズとリンクフェーズの2つのフェーズを区別できます。
DOMが読み込まれると、Angularはコンパイルフェーズを開始します。コンパイルフェーズでは、マークアップを上から下に移動し、compile
すべてのディレクティブを呼び出します。グラフィック的には、次のように表現できます。
この段階で、コンパイル関数が取得するテンプレートはソーステンプレート(インスタンステンプレートではない)であることを言及することはおそらく重要です。
多くの場合、DOMインスタンスは、ソーステンプレートがDOMにレンダリングされた結果ですが、によって作成されるng-repeat
か、その場で導入されます。
ディレクティブを持つ要素の新しいインスタンスがDOMにレンダリングされるたびに、リンクフェーズが開始されます。
このフェーズでは、Angularはcontroller
、を呼び出し、pre-link
子を反復しpost-link
、次のようにすべてのディレクティブを呼び出します。
各種指令機能は、二つ呼ばれる他の角度の関数内から実行される$compile
(ディレクティブのがcompile
実行される)と呼ばれる内部機能nodeLinkFn
(指示者controller
、preLink
及びpostLink
実行されます)。ディレクティブ関数が呼び出される前と後に、角度関数内でさまざまなことが起こります。おそらく最も顕著なのは、子供の再帰です。次の簡略図は、コンパイルおよびリンクフェーズ内の主要な手順を示しています。
これらの手順を示すために、次のHTMLマークアップを使用してみましょう。
<div ng-repeat="i in [0,1,2]">
<my-element>
<div>Inner content</div>
</my-element>
</div>
次のディレクティブで:
myApp.directive( 'myElement', function() {
return {
restrict: 'EA',
transclude: true,
template: '<div>{{label}}<div ng-transclude></div></div>'
}
});
compile
APIのルックスがとても好きです。
compile: function compile( tElement, tAttributes ) { ... }
多くの場合、パラメータの前にプレフィックスを付けてt
、提供される要素と属性がインスタンスの属性ではなく、ソーステンプレートの属性であることを示します。
compile
トランスクルードされたコンテンツ(存在する場合)の呼び出しの前に削除され、テンプレートがマークアップに適用されます。したがって、compile
関数に提供される要素は次のようになります。
<my-element>
<div>
"{{label}}"
<div ng-transclude></div>
</div>
</my-element>
この時点では、変換されたコンテンツは再挿入されないことに注意してください。
ディレクティブのの呼び出しに続いて.compile
、Angularは、ディレクティブによって導入されたばかりの子要素(テンプレート要素など)を含むすべての子要素をトラバースします。
この例では、上記のソーステンプレートの3つのインスタンスが(によってng-repeat
)作成されます。したがって、次のシーケンスは、インスタンスごとに1回ずつ、3回実行されます。
controller
APIが含まれます。
controller: function( $scope, $element, $attrs, $transclude ) { ... }
リンクフェーズに入り、経由$compile
で返されるリンク関数にスコープが提供されるようになりました。
最初に、リンク関数は、要求された場合、子スコープ(scope: true
)または分離スコープ(scope: {...}
)を作成します。
次に、コントローラーが実行され、インスタンス要素のスコープが提供されます。
pre-link
APIのルックスがとても好きです。
function preLink( scope, element, attributes, controller ) { ... }
ディレクティブの呼び出し.controller
と.preLink
関数の間では、実質的に何も起こりません。Angularは、それぞれがどのように使用されるべきかについての推奨をまだ提供しています。
.preLink
呼び出しに続いて、リンク関数は各子要素をトラバースします-正しいリンク関数を呼び出し、それに現在のスコープ(子要素の親スコープとして機能します)をアタッチします。
post-link
APIはpre-link
関数のAPIに似ています。
function postLink( scope, element, attributes, controller ) { ... }
おそらく、ディレクティブの.postLink
関数が呼び出されると、すべての子の.postLink
関数を含む、そのすべての子要素のリンクプロセスが完了したことに注目する価値があります。
これは、.postLink
呼び出されるまでに、子供が「生きている」準備ができていることを意味します。これも:
したがって、この段階でのテンプレートは次のようになります。
<my-element>
<div class="ng-binding">
"{{label}}"
<div ng-transclude>
<div class="ng-scope">Inner content</div>
</div>
</div>
</my-element>
4つすべての関数を使用する場合、ディレクティブは次の形式に従います。
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return {
pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
// Pre-link code goes here
},
post: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
}
};
});
compileはリンク前関数とリンク後関数の両方を含むオブジェクトを返すことに注意してください。Angularの専門用語では、コンパイル関数はテンプレート関数を返します。
場合はpre-link
必要ありません、コンパイル機能は、単純にそうように、代わりに定義オブジェクトの後のリンク機能を返すことができます。
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
};
}
};
});
compile
(post)link
メソッドが定義された後で、メソッドを追加したい場合があります。これには、以下を使用できます。
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return this.link;
},
link: function( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
});
コンパイル関数が必要ない場合は、宣言を完全にスキップしてlink
、ディレクティブの設定オブジェクトのプロパティでポストリンク関数を提供できます。
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
上記のいずれの例でも、controller
必要がなければ、関数を削除できます。たとえば、post-link
関数のみが必要な場合は、次のように使用できます。
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
AngularがDOM操作を許可するという事実は、コンパイルプロセスへの入力マークアップが出力と異なる場合があることを意味します。特に、一部の入力マークアップはng-repeat
、DOMにレンダリングされる前に(のように)数回複製される場合があります。
角度の用語は少し矛盾していますが、2つのタイプのマークアップを区別しています。
次のマークアップはこれを示しています。
<div ng-repeat="i in [0,1,2]">
<my-directive>{{i}}</my-directive>
</div>
ソースhtmlが定義します
<my-directive>{{i}}</my-directive>
ソーステンプレートとして機能します。
ただし、ng-repeat
ディレクティブ内でラップされているため、このソーステンプレートは複製されます(この例では3回)。これらのクローンはインスタンステンプレートであり、それぞれがDOMに表示され、関連するスコープにバインドされます。
各ディレクティブのcompile
関数は、Angularブートストラップ時に一度だけ呼び出されます。
公式には、これはスコープやデータバインディングを含まない(ソースの)テンプレート操作を実行する場所です。
これは主に、最適化の目的で行われます。次のマークアップを検討してください。
<tr ng-repeat="raw in raws">
<my-raw></my-raw>
</tr>
<my-raw>
ディレクティブは、DOMのマークアップの特定のセットをレンダリングします。したがって、次のいずれかを行うことができます。
ng-repeat
(ソース・テンプレートを複製する<my-raw>
)、次いで(外部各インスタンステンプレートのマークアップ変更compile
機能)。compile
関数内の)目的のマークアップを含むようにソーステンプレートを変更し、それng-repeat
を複製できるようにします。raws
コレクションに1000個のアイテムがある場合、後者のオプションは前者のオプションよりも高速になる可能性があります。
各ディレクティブのcontroller
関数は、新しい関連要素がインスタンス化されるたびに呼び出されます。
公式には、controller
機能は次のいずれかです。
繰り返しになりますが、ディレクティブに分離されたスコープが含まれている場合、親スコープから継承するディレクティブ内のプロパティはまだ利用できないことに注意してください。
ときpost-link
結合、トランスクルーなど-関数が呼び出され、以前のすべての手順が行われました
これは通常、レンダリングされたDOMをさらに操作する場所です。
各ディレクティブのpre-link
関数は、新しい関連要素がインスタンス化されるたびに呼び出されます。
コンパイル順序のセクションで前述したように、pre-link
関数は親から子post-link
と呼ばれ、関数はと呼ばれchild-then-parent
ます。
このpre-link
関数はめったに使用されませんが、特別なシナリオで役立ちます。たとえば、子コントローラーがそれ自体を親コントローラーに登録するが、登録はあるparent-then-child
方法で行われる必要がある場合(ngModelController
この方法で行われます)。