angularjs directive 指令使用

directive指令:

var myModule = angular.module(...); 

     myModule.directive('directiveName', function factory(injectables) { 

     var directiveDefinitionObject = { 

   priority: 0,  /***priority(数字),可选参数,指明指令的优先级,若在单个DOM上有多个指令,则优先级高的先执行;****/

   template: '<div></div>',  /**1.(字符串或者函数)可选参数,可以是:html

                   2. 一个函数,可接受两个参数tElementtAttrs; 其中tElement是指使用此指令的元素,而tAttrs则实例的属性,它是一个由元素上所有的属性组成的集合(对象)形如:

                   <hello-world2 title = '我是第二个directive'></hello-world2> template: function(tElement,tAttrs){}**/ 

   templateUrl: 'directive.html', /**可选参数,可以是(1)一个代表HTML文件路径的字符串(2)一个函数,可接受两个参数tElement和tAttrs(大致同上)**/

   replace: false,  /***replace (布尔值),默认值为false,设置为true时候; replace为true时,hello-world这个标签不在了,反之,则存在。***/

   transclude: false, /***如果不想让指令内部的内容被模板替换,可以设置这个值为true。一般情况下需要和ngTransclude指令一起使用。 比如:template:"<div>hello every <div ng-transclude></div></div>",

                这时,指令内部的内容会嵌入到ng-transclude这个div中。这个配置选项可以让我们提取包含在指令那个元素里面的内容,再将它放置在指令模板的特定位置。

                当你开启transclude后,你就可以使用ng-transclude来指明了应该在什么地方放置transcluded内容;

                如果指令使用了transclude参数,那么在控制器无法正常监听数据模型的变化了。建议在链接函数里使用$watch服务。***/

   restrict: 'A',     /***restrict(字符串)可选参数,指明指令在DOM里面以什么形式被声明;取值有:E(元素),A(属性),C(类),M(注释),其中默认值为A;当然也可以两个一起用,比如EA.表示即可以是元素也可以是属性。

                                    E(元素):<directiveName></directiveName>

                                        A(属性):<div directiveName='expression'></div>

                                    C(类): <div class='directiveName'></div>

                                        M(注释):<--directive:directiveName expression-->

              一般情况下E/A/C用得比较多。 **/ 

   scope: false,  /***(1)默认值false。表示继承父作用域; 

             (2)true。表示继承父作用域,并创建自己的作用域(子作用域); 

             (3){}。表示创建一个全新的隔离作用域;当你想要创建一个可重用的组件时隔离作用域是一个很好的选择; 隔离作用域可以通过绑定策略来访问父作用域的属性。 

                directive 在使用隔离 scope 的时候,提供了三种方法同隔离之外的地方交互。这三种分别是:

                @ 绑定一个局部 scope 属性到当前 dom 节点的属性值。结果总是一个字符串,因为 dom 属性是字符串。单向, 父变化==》 子变化

                & 提供一种方式执行一个表达式在父 scope 的上下文中。如果没有指定 attr 名称,则属性名称为相同的本地名称。

                = 通过 directive 的 attr 属性的值在局部 scope 的属性和父 scope 属性名之间建立双向绑定。双向****/ 

   compile: function compile(tElement, tAttrs, transclude) {

     return {

       pre: function preLink(scope, iElement, iAttrs, controller) { ... },

       post: function postLink(scope, iElement, iAttrs, controller) { ... }

    }

              /*** 编译函数是用来处理需要修改模板DOM的情况的。因为大部分指令都不需要修改模板,所以这个函数也不常用。需要用到的例子有ngTrepeat,这个是需要修改模板的,还有ngView这个是需要异步载入内容的。编译函数接受以下参数。***/

         },

       controller:  function($scope, $element, $attrs, $transclude) {

       // 控制器逻辑放在这里

        }

/***可以是一个字符串或者函数。

    若是为字符串,则将字符串当做是控制器的名字,来查找注册在应用中的控制器的构造函数

也可以直接在指令内部的定义为匿名函数,同样我们可以再这里注入任何服务($log,$timeout等等)

(1)$scope,与指令元素相关联的作用域

(2)$element,当前指令对应的 元素

(3)$attrs,由当前元素的属性组成的对象

(4)$transclude,嵌入链接函数,实际被执行用来克隆元素和操作DOM的函数 

控制器主要是用来提供可在指令间复用的行为但link链接函数只能在当前内部指令中定义行为,且无法再指令间复用。 

指令中的controller是用来让不同指令间通信用的。

**/

        controllerAs:'mainController',    /***这个选项的作用是可以设置你的控制器的别名***/ 

        require:'',   /*** require(字符串或者数组)

字符串代表另一个指令的名字,它将会作为link函数的第四个参数。具体用法我们可以举个例子说明。假设现在我们要编写两个指令,两个指令中的link链接函数中(link函数后面会讲)存在有很多重合的方法,

这时候我们就可以将这些重复的方法写在第三个指令的controller中(上面也讲到controller经常用来提供指令间的复用行为)然后在这两个指令中,require这个拥有controller字段的的指令(第三个指令),

最后通过link链接函数的第四个参数就可以引用这些重合的方法了。 

(1)没有前缀,指令会在自身提供的控制器中进行查找,如果找不到任何控制器,则会抛出一个error

(2)?如果在当前的指令没有找到所需的控制器,则会将null传给link连接函数的第四个参数

(3)^如果在当前的指令没有找到所需的控制器,则会查找父元素的控制器

(4)?^组合***/

        link: function postLink(scope, iElement, iAttrs) { ... }

/***link函数就会被执行,主要做数据绑定,通过在DOM上注册监听器来动态修改scope中的数据,或者是使用$watchs监听 scope中的变量来修改DOM,从而建立双向绑定等等。若我们的指令中没有配置compile函数,那我们配置的link函数就会运行

 在指令中compile与link选项是互斥的,如果同时设置了这两个选项,那么就会把compile所返回的函数当做是链接函数,而link选项本身就会被忽略掉链接函数负责注册DOM事件和更新DOM。它是在模板被克隆之后执行的,它也是大部分指令逻辑代码编写的地方。

 scope - 指令需要监听的作用域。

 iElement - instance element - 指令所在的元素。只有在postLink函数中对元素的子元素进行操作才是安全的,因为那时它们才已经全部链接好。

 iAttrs - instance attributes - 实例属性,一个标准化的、所有声明在当前元素上的属性列表,这些属性在所有链接函数间是共享的。

 controller - 控制器实例,也就是当前指令通过require请求的指令direct2内部的controller。

 ***/

}; 

 return directiveDefinitionObject; 

})

 Anguar的指令编译过程

首先加载angularjs库,查找到ng-app指令,从而找到应用的边界,

根据ng-app划定的作用域来调用$compile服务进行编译,angularjs会遍历整个HTML文档,并根据js中指令的定义来处理在页面上声明的各个指令按照指令的优先级(priority)排列,

根据指令中的配置参数(template,place,transclude等)转换DOM然后就开始按顺序执行各指令的compile函数(如果指令上有定义compile函数)对模板自身进行转换

 注意:此处的compile函数是我们指令中配置的,跟上面说的$compile服务不一样。每个compile函数执行完后都会返回一个link函数,所有的link函数会合成一个大的link函数

 然后这个大的link函数就会被执行,主要做数据绑定,通过在DOM上注册监听器来动态修改scope中的数据,或者是使用$watchs监听 scope中的变量来修改DOM,从而建立双向绑定等等。

 若我们的指令中没有配置compile函数,那我们配置的link函数就会运行,她做的事情大致跟上面complie返回之后所有的link函数合成的的大的link函数差不多。

 所以:在指令中compile与link选项是互斥的,如果同时设置了这两个选项,那么就会把compile所返回的函数当做是链接函数,而link选项本身就会被忽略掉

 编译函数 Compile function

function compile(tElement, tAttrs, transclude) { ... }

编译函数是用来处理需要修改模板DOM的情况的。因为大部分指令都不需要修改模板,所以这个函数也不常用。需要用到的例子有ngTrepeat,这个是需要修改模板的,还有ngView这个是需要异步载入内容的。编译函数接受以下参数。

tElement - template element - 指令所在的元素。对这个元素及其子元素进行变形之类的操作是安全的。

tAttrs - template attributes - 这个元素上所有指令声明的属性,这些属性都是在编译函数里共享的。

transclude - 一个嵌入的链接函数function(scope, cloneLinkingFn)。

注意:在编译函数里面不要进行任何DOM变形之外的操作。 更重要的,DOM监听事件的注册应该在链接函数中做,而不是编译函数中。

编译函数可以返回一个对象或者函数。

返回函数 - 等效于在编译函数不存在时,使用配置对象的link属性注册的链接函数。

返回对象 - 返回一个通过pre或post属性注册了函数的对象。参考下面pre-linking和post-liking函数的解释。

 链接函数 Linking function

function link(scope, iElement, iAttrs, controller) { ... }

链接函数负责注册DOM事件和更新DOM。它是在模板被克隆之后执行的,它也是大部分指令逻辑代码编写的地方。

scope - 指令需要监听的作用域。

iElement - instance element - 指令所在的元素。只有在postLink函数中对元素的子元素进行操作才是安全的,因为那时它们才已经全部链接好。

iAttrs - instance attributes - 实例属性,一个标准化的、所有声明在当前元素上的属性列表,这些属性在所有链接函数间是共享的。

controller - 控制器实例,也就是当前指令通过require请求的指令direct2内部的controller。

比如:direct2指令中的controller:function(){this.addStrength = function(){}},那么,在当前指令的link函数中,你就可以通过controller.addStrength进行调用了。

Pre-linking function 在子元素被链接前执行。不能用来进行DOM的变形,以防链接函数找不到正确的元素来链接。

Post-linking function 所有元素都被链接后执行。

 说明:

 compile选项本身并不会被频繁使用,但是link函数则会被经常使用。本质上,当我们设置了link选项,实际上是创建了一个postLink() 链接函数,以便compile() 函数可以定义链接函数。

通常情况下,如果设置了compile函数,说明我们希望在指令和实时数据被放到DOM中之前进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。

compile和link选项是互斥的。如果同时设置了这两个选项,那么会把compile所返回的函数当作链接函数,而link选项本身则会被忽略。译函数负责对模板DOM进行转换。

链接函数负责将作用域和DOM进行链接。 在作用域同DOM链接之前可以手动操作DOM。在实践中,编写自定义指令时这种操作是非常罕见的,但有几个内置指令提供了这样的功能。

链接函数是可选的。如果定义了编译函数,它会返回链接函数,因此当两个函数都定义时,编译函数会重载链接函数。

如果我们的指令很简单,并且不需要额外的设置,可以从工厂函数(回调函数)返回一个函数来代替对象。如果这样做了,这个函数就是链接函数。

posted @ 2020-11-07 18:34  荆棘中的百合花  阅读(238)  评论(0编辑  收藏  举报