1 app.directive('helloWorld', function() { 2 return { 3 restrict: 'AE', 4 priority: 1, 5 replace: 'true', 6 scope: {}, 7 template: '<h3>Hello World!!</h3>', 8 templateUrl: '', 9 compile: function(tElem, attrs) { 10 //do optional DOM transformation here 11 return function(scope, elem, attrs) { 12 //linking function here 13 } 14 }, 15 link: function(scope, elem, attrs) { 16 //write your functions 17 }, 18 require: '^otherDirective', 19 controller: function($scope, $compile, $http) { 20 // $scope is the appropriate scope for the directive 21 this.addChild = function(nestedDirective) { // this refers to the controller 22 console.log(nestedDirective.message); 23 }; 24 } 25 } 26 });
priority
当有多个directive定义在同一个DOM元素时,有时需要明确它们的执行顺序。这属性用于在directive的compile function调用之前进行排序。如果优先级相同,则执行顺序是不确定的, 读者可以尝试.
restrict
设置指令在HTML如何作用
参数列表:
A - attribute
C - class
E - element
M - comment
replace
设置加载template/templateUrl时, 对directive自身这个标签的处理
参数列表:
false: 默认值, 保留这个标签的html代码
true: 用template/templateUrl的内容替换个这个directive标签
如:
1 <div hello-directive></div> 2 { 3 replace: true, 4 template: '<div>hi</div>' 5 }
页面结果为:
1 <div>hi</div>
反之(replace=false):
1 <div hello-directive> 2 <div>hi</div> 3 </div>
scope
默认情况下,指令获取它父节点的controller的scope.
参数列表:
false - 默认值, 会影响父scope
true - 创建一个子scope原型 继承 父scope
{} - 隔离的scope,一个孤立存在不继承自父scope的scope
隔离scope和父scope之间的数据绑定
选择一:使用 @ 实现单向文本绑定, 父类可以修改子类
HTML:
1 <body ng-controller="MainCtrl"> 2 <input type="text" ng-model="color" placeholder="Enter a color"/> 3 <hello-world color-attr="{{color}}"/> 注意内容的写法是带"{{}}",用@进行单向绑定 4 </body>
JS:
1 app.directive('helloWorld', function() { 2 return { 3 scope: { 4 color: '@colorAttr' //当隔离scope属性和指令元素参数的名字一样时,可以只写'@' 5 }, 6 .... 7 // the rest of the configurations 8 }; 9 });
选择二:使用 = 实现双向绑定
HTML:
1 <body ng-controller="MainCtrl"> 2 <input type="text" ng-model="color" placeholder="Enter a color"/> 3 <hello-world color="color"/> 注意内容的写法是不带"{{}}",直接写model名,用=进行双向绑定 4 </body>
JS:
1 app.directive('helloWorld', function() { 2 return { 3 scope: { 4 color: '=' 5 }, 6 .... 7 // the rest of the configurations 8 }; 9 });
选择三:使用 & 在父scope中执行函数
HTML:
1 <body ng-controller="MainCtrl"> 2 <input type="text" ng-model="color" placeholder="Enter a color"/> 3 <say-hello sayHelloIsolated="sayHello()"/> 执行父scope中的sayHello()方法 4 </body>
JS:
1 app.directive('sayHello', function() { 2 return { 3 scope: { 4 sayHelloIsolated: '&' 5 }, 6 .... 7 // the rest of the configurations 8 }; 9 });
Transclusion(插入)
把template/templateUrl插入指令模板中指定的位置
参数列表:
true - 结合ng-transclude指令使用
element - 待研究
HTML:
1 <div output-text> 2 <p>This is div in index.html</p> 3 </div>
JS:
1 app.directive('outputText', function() { 2 return { 3 transclude: true,4 template: '<div ng-transclude></div><div>Include before this</div>' 5 }; 6 });
结果:
This is div in index.html
Link函数
link函数主要用来为DOM元素添加事件监听、监视模型属性变化、以及更新DOM。实际上directive返回的对象就是个link,你可以直接return function(scope,elem,attrs){}来简写directive.
link: function(scope, elem, attrs) { }
参数列表:
- scope – 指令的scope。默认是父类的scope, 如果自己有自定义scope, 则指自己定义的.
- elem – 指令的jQLite(jQuery的子集)包装DOM元素。如果你在引入AngularJS之前引入了jQuery,那么这个元素就是jQuery元素,而不是jQLite元素。由于这个元素已经被jQuery/jQLite包装了,所以我们就 在进行DOM操作的时候就不需要再使用 $()来进行包装。
- attr – 一个包含了指令所在元素的属性的标准化的参数对象。举个例子,你给一个HTML元素添加了一些属性:,那么可以在 link 函数中通过 attrs.someAttribute 来使用它。
compile函数
compile 函数在 link 函数被执行之前用来做一些DOM改造。
要注意的是 compile 函数不能访问 scope,并且必须返回一个 link 函数。但是如果没有设置 compile 函数,你可以正常地配置 link 函数,如果有了compile,就不能用link,link函数由compile返回.
大多数的情况下,你只需要使用 link 函数。这是因为大部分的指令只需要考虑注册事件监听、监视模型、以及更新DOM等,这些都可以在 link 函数中完成。 但是对于像 ng-repeat 之类的指令,需要克隆和重复 DOM 元素多次,在 link 函数执行之前由 compile 函数来完成。这就带来了一个问题,为什么我们需要两个分开的函数来完成生成过程,为什么不能只使用一个?要回答好这个问题,我们需要理解指令在Angular中是如何被编译的!
指令是如何被编译的
当应用引导启动的时候,Angular开始使用 $compile 服务遍历DOM元素。这个服务基于注册过的指令在标记文本中搜索指令。一旦所有的指令都被识别后,Angular执行他们的 compile 方法。如前面所讲的,compile 方法返回一个 link 函数,被添加到稍后执行的 link 函数列表中。这被称为编译阶段。如果一个指令需要被克隆很多次(比如 ng-repeat),compile函数只在编译阶段被执行一次,复制这些模板,但是link 函数会针对每个被复制的实例被执行。所以分开处理,让我们在性能上有一定的提高。这也说明了为什么在 compile 函数中不能访问到scope对象。 在编译阶段之后,就开始了链接(linking)阶段。在这个阶段,所有收集的 link 函数将被一一执行。指令创造出来的模板会在正确的scope下被解析和处理,然后返回具有事件响应的真实的DOM节点。
controller 函数和 require
如果你想要允许其他的指令和你的指令发生交互时,你需要使用 controller 函数。比如有些情况下,你需要通过组合两个指令来实现一个UI组件。那么你可以通过如下的方式来给指令添加一个 controller 函数。
app.directive('outerDirective', function() { return { scope: {}, restrict: 'AE', controller: function($scope, $compile, $http) { // $scope is the appropriate scope for the directive this.addChild = function(nestedDirective) { // this refers to the controller console.log('Got the message from nested directive:' + nestedDirective.message); }; } }; });
这个代码为指令添加了一个名叫 outerDirective 的controller。当另一个指令想要交互时,它需要声明它对你的指令 controller 实例的引用(require)。可以通过如下的方式实现:
app.directive('innerDirective', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', link: function(scope, elem, attrs, controllerInstance) { //the fourth argument is the controller instance you require scope.message = "Hi, Parent directive"; controllerInstance.addChild(scope); } }; });
相应的HTML代码如下:
<outer-directive> <inner-directive></inner-directive> </outer-directive>
require:
‘^outerDirective’ 告诉Angular在元素以及它的父元素中搜索controller。这样被找到的 controller 实例会作为第四个参数被传入到 link 函数中。在我们的例子中,我们将嵌入的指令的scope发送给父亲指令。
如果找不到这个名称对应的controller,那么将会抛出一个error。
名称可以加入以下前缀:
? - 不要抛出异常。这使这个依赖变为一个可选项。
^ - 允许查找父元素的controller
如果你想尝试这个代码的话,请在开启浏览器控制台的情况下打开这个Plunker。同时,这篇Angular官方文档上的最后部分给了一个非常好的关于指令交互的例子,是非常值得一读的。
controller,link,compile执行顺序
如果有compile: controller先运行,compile后运行,link不运行
如果没有compile: controller先运行,link后运行,link和compile不兼容, 前文已说过.
参考文档:
http://blog.jobbole.com/62249/
http://blog.jobbole.com/62999/
http://developer.51cto.com/art/201403/431734.htm