まず、ディレクティブを登録するための API について説明します。コントローラーと同様に、ディレクティブもモジュールに登録されます。ディレクティブを登録するには、module.directive API を使用する必要があります。 module.directive は、標準化されたディレクティブ名とそれに続くファクトリ関数を受け入れます。このファクトリー関数は、一致したときにどのように動作するかを $compile ディレクティブに伝えるために、さまざまなオプションを持つオブジェクトを返す必要があります。 ファクトリー関数は、$conpile がディレクティブに初めて一致したときに 1 回だけ呼び出されます。ここで初期化作業を指示できます。この (ファクトリー) 関数は $injector.invoke を使用して呼び出され、コントローラーのように注入可能になります。
ディレクティブを作成した場合、HTML7 で同じ要素が導入されると問題が生じます。 2 文字または 3 文字の接頭辞 (例: btfCarousel) は適切に機能します。同様に、独自のディレクティブに ng を接頭辞として付けないでください。そうしないと、AngularJS の将来のバージョンに含まれるディレクティブと競合する可能性があります。
フォローアップの例として、私のプレフィックス (例: myCustomer)。
テンプレート展開ディレクティブ
顧客情報を表す大きなテンプレートがあると仮定します。このテンプレートはコード内で複数回繰り返されます。 1 か所で変更すると、他のいくつかの場所でも変更する必要があります。これは、ディレクティブを使用してテンプレートを簡素化する良い機会です。
コンテンツを静的テンプレートに置き換えるだけのディレクティブを作成しましょう:
このディレクティブにはバインディングがあることに注意してください。 $compile が をコンパイルしてリンクした後、要素の子要素のディレクティブとの一致を試みます。これは、ディレクティブ内にディレクティブを構築できることを意味します (ネストされたディレクティブ)。例の書き方については後ほど説明します。
在上面的例子中,我们列出了模板选项(template attribute of return object in factory function),但随着模板大小的增长,这将变得令人讨厌。
Best Practice: Unless your template is very small, it's typically better to break it apart into its own HTML file and load it with the templateUrl option.
如果你熟悉ngInclude,templateUrl就像它一样工作。下面是使用templateUrl代替的相同示例:
templateUrl也可以是一个函数,它返回要加载和用于指令的HTML模板的URL。AngularJS将使用两个参数调用templateUrl函数:指令被调用的元素以及与该元素相关联的attr对象。
Note: You do not currently have the ability to access scope variables from the templateUrl function, since the template is requested before the scope is initialized
注:(要访问socpe上的值,应该在post-link阶段).
When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template.The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.
用元素来使用myCustomer指令时明智的选择,因为你不用一些“customer”行为修饰一个元素,你定义一个元素核心行为作为一个costomer组建。(想看更多就到PHP中文网angularjs参考手册中学习)
隔离指令的Scope
我们以上的myCustomer指令很好,但是它有一个致命缺陷。我们只有在一个给定的scope下使用。
在其目前的实现上,我们应该需要去创建一些不同点控制器用来重用这个指令。
https://plnkr.co/edit/CKEgb1e...
这明显不是一个好的解决方案。
我们说项的是把指令内部的scope与外部scope(controller scope)分离,并且映射外部scope到指令内部scope。我们可以通过创建一个isolate scope来做。为此,我们可以使用指令的scope选项。
https://plnkr.co/edit/E6dTrgm...
看index.html文件,第一个元素绑定info属性值为naomi,它是我们已经暴露在我们的控制器上的scope。第二个绑定info为igor。
让我们仔细看看scope选项
//...
scope: { customerInfo: '=info' },
//...
除了可以将不同的数据绑定到指令中的作用域外,使用isolated scope还有其他作用。
我们可以通过添加另一个属性vojta来展示,到我们的scope并尝试从我们的指令模板中访问它:
https://plnkr.co/edit/xLVqnzt...
请注意{{vojta.name}}和{{vojta.address}}为空,意味着它们未定义(undefined)。虽然我们在控制器中定义了vojta,但它在指令中不可用。
顾名思义,该指令的 isolate scope隔离了除显式添加到作用域的模型之外的所有内容:scope: {}散列对象. 这在构建可重用组件时很有用,因为它可以防止组件改变模型状态,除了显式传入。
Note: Normally, a scope prototypically inherits from its parent. An isolated scope does not. See the "Directive Definition Object - scope"section for more information about isolate scopes.
Best Practice: Use the scope option to create isolate scopes when making components that you want to reuse throughout your app.
创建一个操纵DOM的指令
在这个例子中,我们将建立一个显示当前时间的指令。每秒一次,它会更新DOM以反映当前时间。
想要修改DOM的指令通常使用link选项来注册DOM监听器以及更新DOM。它在模板被克隆之后执行,并且是放置指令逻辑的地方。
link接受一个带有一下签名的函数function link(scope, element, attrs, controller, transcludeFn) { ... }, 其中:
scope是一个Angularjs scope 对象
element 是一个此指令匹配的jqLite包装元素
attrs是一个具有标准化属性名称及其对应属性值的键值对的散列对象。
controller是指令所需的控制器实例或其自己的控制器(如果有的话)。确切的值取决于指令的 require属性。
transcludeFn是预先绑定到正确的包含范围的transclude链接函数。
For more details on the link option refer to the $compile API page.
リンク関数では、表示時刻を毎秒更新するか、ユーザーがコマンドにバインドされている時刻形式文字列を変更する必要があります。 $interval サービスを使用してハンドラーを定期的に呼び出します。これは $timeout を使用するよりも簡単ですが、テストを完了する前にすべての $timeout が完了していることを確認したいエンドツーエンドのテストにも適しています。命令が削除された場合は、メモリリークが発生しないように $interval も削除する必要があります
https://plnkr.co/edit/vIhhmNp...
ここで注意すべき点がいくつかあります。 module.controller API と同様に、module.directive の関数パラメーターには依存関係が挿入されます。したがって、ディレクティブのリンク関数で $interval と dateFilter を使用できます。
イベント element.on('$destroy', ...) を登録します。この $destroy イベントは何がトリガーするのでしょうか?
AngularJS はいくつかの特別なイベントを公開します。 AngularJS のコンパイラでコンパイルされた DOM ノードが破棄されると、$destroy イベントが発生します。同様に、Angularjs スコープが破棄されると、$destroy イベントがリスニング スコープにブロードキャストされます。
このイベントをリッスンすることで、メモリリークを引き起こす可能性のあるイベントリスナーを削除できます。スコープと要素に登録されたリスニング イベントは、DOM が破棄されると自動的にクリーンアップされますが、サービスにリスナーを登録した場合、または削除されていない DOM ノードにリスナーを登録した場合は、自分でクリーンアップする必要があります。そうでない場合は、メモリリークを引き起こすリスクがあります。
ベスト プラクティス: ディレクティブはそれ自体の後にクリーンアップする必要があります。クリーンアップを実行するには、element.on('$destroy', ...) またはscope.$on('$destroy', ...) を使用できます。ディレクティブが削除されたときの関数。
他の要素をラップするディレクティブを作成する
分離スコープを使用してモデルをディレクティブに渡すことができることを見てきましたが、代わりにテンプレート全体を渡したい場合もあります。文字列またはオブジェクトの。 「ダイアログ ボックス」コンポーネントを作成したいとします。ダイアログ ボックスには、任意のコンテンツをラップする機能が必要です。
これには、transclude オプションを使用する必要があります。
transclude オプションは具体的に何をするのですか? transclude は、このオプションを通じて、内部スコープではなく外部ディレクティブのスコープに対してディレクティブの内容にアクセスできるようにします。
この点を説明するには、以下の例を参照してください。 script.js にリンク関数を追加し、名前を Jeff に再定義していることに注目してください。 {{name}} バインディングは何を達成すると思いますか?
いつものように、私たちは {{name}} がジェフであるべきだと考えました。しかし、私たちが見ているのはトビアスです。
transclude オプションは、スコープのネスト方法を変更します。これにより、トランスクルードされたディレクティブのコンテンツに、内部スコープではなく、ディレクティブの外側にスコープ コンテンツが含まれるようになります。そうすることで、外部スコープからコンテンツにアクセスできるようになります。
ディレクティブが独自の独立したスコープを作成しない場合、scope.name = 'Jeff' のスコープは外側のスコープを参照し、出力には Jeff が表示されることに注意してください。
この動作は、何かをカプセル化するディレクティブには意味があります。それ以外の場合は、使用する各モデルを個別に渡す必要があるからです。必要なすべてのモデルを渡す必要がある場合、実際には任意のコンテンツを使用することはできません。
ベスト プラクティス: 任意のコンテンツをラップするディレクティブを作成する場合のみ、transclude: true を使用してください。
次に、このダイアログにボタンを追加し、ディレクティブを使用するユーザーが動作を変更できるようにします。このダイアログボックスに移動します。
ディレクティブのスコープから呼び出して渡した関数を実行したいのですが、登録されたスコープのコンテキストで実行されます。
スコープ オプションで =attr を使用する方法を前に見てきましたが、上記の例では代わりに &attr を使用しました。 & バインディングを使用すると、ディレクティブで特定の時点でプリミティブ スコープ内の式の評価をトリガーできます。関数呼び出しを含むあらゆる正当な表現が許可されます。そのため、& バインディングはコールバック関数をディレクティブ アクションにバインドするのに最適です。
ユーザーがダイアログで [x] をクリックすると、ng-click のおかげでディレクティブの close 関数が呼び出されます。分離されたスコープでのこの close 呼び出しは、実際には元のスコープのコンテキストで式 hideDialog(message) を評価し、コントローラーの HideDialog 関数が実行されます。
通常、式を介して分離スコープから親スコープにデータを渡すことが期待されます。これは、ローカル変数名と値のマップを式ラッパー関数に渡すことによって行うことができます。たとえば、hideDialkog 関数は、ダイアログが非表示になっているときに表示するメッセージを受け取ります。これは、ディレクティブ呼び出し close({message: 'closeing for now'}) によって示されます。その後、on-close 式内でローカル変数メッセージがアクセスされます (利用可能になります)。
ベスト プラクティス: ディレクティブで動作にバインドするための API を公開する場合は、スコープ オプションで &attr を使用します。
メッセージを作成します。イベント リスナーを追加するディレクティブ
以前は、リンク関数を使用して DOM 要素を操作するディレクティブを作成していました。この例を基にして、要素上のイベントに反応するディレクティブを作成してみましょう。
たとえば、ユーザーが要素をドラッグできるようにするディレクティブを作成したい場合はどうすればよいでしょうか?
创建一个通信的指令
你可以组建任何指令通过模板使用他们。
有时,你需要一个由指令组合构建的组件。
想象你想要有一个容器,其中容器的内容对应于哪个选项卡处于活动状态的选项卡。
myPane指令有require选项值为^^myTabs. 当指令使用此选项,&compile将抛出一个错误除非特定的controller被找到。 ^^前缀表示该指令在其父元素上搜索控制器。(^前缀将使指令在自身元素或她的父元素上寻找控制器;又没任何前缀,指令将值操作自身)
所以这个myTabs contoller从哪里来的?指令可以特定一个controllers通过使用 controller选项。如你所见,myTabs指令使用了此选项。就像ngController,此选项附加一个控制器到指令的模板上。
如果需要从模板中引用控制器或绑定到控制器的任何功能,则可以使用选项controllerAs将控制器的名称指定为别名。该指令需要定义要使用的此配置的范围。这在指令被用作组件的情况下特别有用。
回头看myPane的定义,注意到link函数的最后一个参数:tabCtrl。当指令需要控制器时,它将接收该控制器作为其link函数的第四个参数。利用这一点,myPane可以调用myTabs的addPane函数。
如果需要多个控制器,则指令的require选项可以采用数组参数。发送给链接函数的相应参数也将是一个数组。
angular.module('docsTabsExample', [])
.directive('myPane', function() {
return {
require: ['^^myTabs', 'ngModel'],
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function(scope, element, attrs, controllers) {
var tabsCtrl = controllers[0],
modelCtrl = controllers[1];
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
明的读者可能想知道链接和控制器之间的区别。基本的区别是控制器可以暴露一个API,并且链接函数可以使用require与控制器交互。
Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.
总结
到此我们已经看了大多数指令的用法,每一个样例演示了一个创建你自己指令的好的起始点。
你可能深入感兴趣于编译过程的解释可以在这里获得compiler guide.
$compile API 有一个全面的指令清单选项以供参考。
最后就是本篇文章到这结束了(想看更多就到PHP中文网angularjs学习手册中学习),有问题的可以在下方留言提问