对于angular 指令的理解:html
好比你想建立一个指令 想被调用的人经过
jquery
<my-directive></my-direvtive>
的话,对于属性就能够设置为 restrict : E(元素)express
若是是
数组
<div my-directive> </div>
restrict : A(属性,默认值)app
若是是:
dom
<div class=“directive:expression;”> </div>
restrict : C(类名)函数
<-- directive:my-directive expression -->
restrict : M(注释)post
能够混用,也能够单独使用。性能
@ @attr 本地做用域属性 = =attr 双向属性 & &attr 父级做用域绑定
ps:ui
对于 & 父级做用域: 传递进来的参数必须是父级的函数方法, 而后在指令中,经过 test() 获取到 传递进来的函数,这个还不够,还必须再执行一次 test()() 才是真正的执行这个方法。
或者在指令的调用上面,直接写好test(t),而后在指令中调用的时候 ,$scope.test({t:'fda'}) 就能够了。
对于 @ 本地做用域: 只能传递字符串进来,对于方法或者对象是传递不进来的。
对于 = 双向属性: 能够传递对象进来,也能够是字符串,可是不能传递方法。 同时能够在 指令中修改这个对象,父级里面的这个对象也会跟着修改的。
//directives.js增长exampleDirective phonecatDirectives.directive('exampleDirective', function() { return { restrict: 'E', template: '<p>Hello {{number}}!</p>', controller: function($scope, $element){ $scope.number = $scope.number + "22222 "; }, link: function(scope, el, attr) { scope.number = scope.number + "33333 "; }, compile: function(element, attributes) { return { pre: function preLink(scope, element, attributes) { scope.number = scope.number + "44444 "; }, post: function postLink(scope, element, attributes) { scope.number = scope.number + "55555 "; } }; } } }); //controller.js添加 dtControllers.controller('directive2',['$scope', function($scope) { $scope.number = '1111 '; } ]); //html <body ng-app="phonecatApp"> <div ng-controller="directive2"> <example-directive></example-directive> </div> </body> // 运行结果。 Hello 1111 22222 44444 55555 ! 由结果能够看出来,controller先运行,compile后运行,link不运行。 将上例中的compile注释掉 // compile: function(element, attributes) { // return { // pre: function preLink(scope, element, attributes) { // scope.number = scope.number + "44444 "; // }, // post: function postLink(scope, element, attributes) { // scope.number = scope.number + "55555 "; // } // }; // } // 结果。 Hello 1111 22222 33333 ! 由结果能够看出来,controller先运行,link后运行,link和compile不兼容。
对于 compile (编译) link(连接),先编译,后链接,可是有了编译以后,对于连接就起不到到做用了。
在compile 阶段主要是实现:标签的解析和替换。 在编译阶段是获取不到 scope 的。主要参数(element.attrbutes)
在link 阶段主要是实现:数据绑定等操做。在link阶段是能够获取到scope 的 主要参数是(scope,element,attrs);
对于一个指令,若是你想将当前指令的API暴露给其余指令使用的时候,可使用controller,不然建议使用link。
参数:字符串或者数组
? 若是在当前指令中没有找到所需的控制器,就会将null传递给link函数中的第四个参数
^ 若是添加了这个前缀,指令会在上游的指令链中查找require参数所指定的控制器
?^ 合并了前面两个说法
没有前缀 指令就会自身提供的控制器中查找,若是没有查找到,就会抛出异常来。
返回一个对象或者函数。
与link 是互斥的,优先级高于 link 。
能够理解成:compile函数在link函数被执行以前用来作一些DOM改造的。
若是有了compile函数,就不能有link 函数了,link函数是由compile函数返回的。
要注意的是 compile 是不能访问scope的,而且返回一link函数。
compile:function(tElem,attrs){ // tElem:指令所在的元素 // attrs: 元素上赋予的参数的标准化列表 // 在这边能够作一些对于dom元素的操做 return function(scope,elem,attrs){ } }
因此,大多数状况下,你只须要link 函数,由于大部分的指令只须要考虑注册事件的监听,监听模型,以及更新DOM等,这些都是能够在link函数中完成的,可是对于像ng-repeat之类的指令,须要克隆和重复dom元素屡次,在link函数执行以前由compile函数来完成。
指令生成出的模板其实没有太多的意义,除非他在特定的scope下编译,默认状况下,指令并不会建立新的子scope
,更多的,他是使用父scope,也就是说,若是指令存在于一个controller下,他就会使用controller的scope,如何运行
scope,咱们须要用到一个link函数,他由指令定义对象中的link属性配置。
link:function(scope,elem,attrs){ // scope:父亲controller的scope // elem:jqLite(jquery的子集)包装的dom元素,不须要再使用$()包装了。 // attrs:一个包含了指令所在元素的属性的标准化的参数对象, // 例子:好比你在html中添加一些属性,那么能够在link函数中经过attrs.someAttribute来使用他。 }
因此说:link 函数主要是为dom 元素添加事件监听,监听模型属性变化,以及更新dom。
另外 若是 有参数 require 的话,对于link 函数方面就会多一个参数 controller
{ require:'?ngModal', link:function(scope,element,attrs,ctrl){ // 这里 ctrl 就是 require 进来的ctrl } }
若是require的参数是数组的话。
{ require:['?ngModal','?test'], link:function(scope,element,attrs,ctrls){ // 这里 ctrl 就是 require 进来的ctrl var modalCtrl = ctrls[0]; var testCtrl = ctrls[1]; } }
在应用引导启动的时候,ng开始使用$compile服务遍历dom元素,这个服务基于注册过的指令在标记文本中搜索指令,一旦全部的指令被识别后,ng执行他们的compile方法,正如前面所讲的,compile 方法返回一个link函数,被添加到稍后执行的link函数列表中,这个阶段被称为编译阶段。若是指令须要被克隆屡次,好比ng-repeat,compile函数只会在编译阶段被执行一次,复制这些模板,可是link函数会针对于每一个被复制的实例被执行。因此分开来处理,让咱们在性能方面有必定的提高。
这也说明了为何在compile函数中不能访问scope对象。在编译阶段之后,就开始了连接阶段,在这个阶段,全部的link函数被一一执行。指令创造出来的模板会在正确的scope下被解析和处理。而后返回具备事件响应的真实的dom节点。
默认状况下,指令获取他的父节点的controller的scope,但这并不适用全部状况。若是将父controller的scope暴露给指令,那么他们能够随意修改scope的属性,在某些状况下,你的指令但愿可以添加一些仅限内部使用的属性和方法,若是咱们在父的scope中添加,会污染父scope,其实咱们还能够有两个选择。
一个子scope,这个scope原型继承于父scope
一个隔离的scope,一个孤立存在不继承父scope的scope
这样的scope能够经过指令对象中的scope属性老配置
scope:true // 建立了一个继承父scope的新的子
scope:{} // 建立了一个隔离的scope,隔离的scope在咱们想建立可重用的指令的时候是很是有好处的。 经过使用隔离的scope,咱们可以保证咱们的指令是自包含的,能够被很容易的插入到HTML中,他内部不能访问父的scope,因此保证了父scope不被污染。
有时候咱们要嵌入指令元素自己,而不只仅是他的内容,这种状况下,咱们须要使用transclude:"element",和transclude:true不一样,他讲标记了ng-transclude指令的元素一块儿包含到了指令模板中,使用transclusion, 你的link函数会获取到一个叫transclude的连接函数,这个函数绑定了正确的指令scope,而且传入了另一个拥有被嵌入dom元素拷贝的函数,你能够在这个transclude函数中执行好比修改元素拷贝或者将他添加到dom上等操。
若是你须要容许其余指令和你的指令发生交互的话,你须要使用controller函数,好比有些状况下,你须要经过组合两个指令来实现一个ui组件,那么你能够经过以下使用
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); }; } }; });
当另一个指令须要使用到这个controller的时候
app.directive('innerDirective', function() { return { scope: {}, restrict: 'AE', require: '^outerDirective', // require controller 方法 // link 参数中就会多一个参数 就是那个require 进来的controller link: function(scope, elem, attrs, controllerInstance) { //the fourth argument is the controller instance you require scope.message = "Hi, Parent directive"; controllerInstance.addChild(scope); } }; });
若是设置 repace : true 的话,就会隐藏掉 对于 指令命名的html 标签
相似 <hello> </hello> 若是replace 设置为true的话,就会消失不显示。
若是设置了transclude 为true的话,就会把本来指令标签中用于写的东西放置到 ng-transclude 中去。
<div ng-transclude> 方式获取到的内容</div>
若是设置了transclude 为true的话,除了可使用ng-transclude 指令, 若是你还须要对本来的内容进行替换的话
能够在link 或者controller里面添加一个参数$transclude
link:function($scope,$element,$attrs,controller,$transclude){ // clone 参数就是用户输入进去的内容。 $transclude(function(clone){ // 进行其余操做。 }); }
案例
// html <div my-link value=" target="_blank">百度</div> // js var app = angular.module('app',[]); app.directive('myLink',function(){ return { restrict:'EA', transclude:true, controller:function($scope,$element,$attrs,$transclude){ $transclude(function(clone){ var a = angular.element('<a>'); a.attr('href',$attrs.value); a.attr('target',$attrs.target); a.text(clone.text()); $element.append(a); }) } } })
对于前缀 ? ^ ?^ 空
若是为空的话,表示在当前指令中查找
若是为?的话,表示没有查找到的话,就会以null做为link 函数的第四个参数
若是为^ 的话,表示能够指向上游查找指令。
若是
require:['?^add','?^minor']
的话,
会致使 link 方法影响
link:function(scope,element,attrs,resultCtrl){ // add : resultCtrl[0]; // minor: resultCtrl[1]; }
若是想调用指令的方法,须要在对应指令中的controller 中定义方法
app.directive('add',function(){ return { controller:function(){ this.add = function(){ } } } })
则调用的时候就须要
resultCtrl[0].add() 就能够调用了。
因为 compile 和link 共同存在的话,只会触发compile 方法。而link 方法是不被触发执行的。
而能够经过这种方式:
{ restrict:'EA', compile:function(element,attrs){ // 编译期间作的事情。 return function link(scope,element,attrs,controller){ // link 函数作的事情。 } } }