1、编译阶段
第一个阶段是编译阶段。在编译阶段, AngularJS会遍历整个HTML文档并根据JavaScript中的指令定义来处理页面上声明的指令。
一旦对指令和其中的子模板进行遍历或编译,编译后的模板会返回一个叫做模板函数的函数。我们有机会在指令的模板函数被返回前,对编译后的DOM树进行修改。
在这个时间点DOM树还没有进行数据绑定,意味着如果此时对DOM树进行操作只会有很少的性能开销。基于此点, ng-repeat和ng-transclude等内置指令会在这个时候,也就是还未与任何作用域数据进行绑定时对DOM进行操作。
2、compile(对象或函数)
compile选项可以返回一个对象或函数。
compile选项本身并不会被频繁使用,但是link函数则会被经常使用。本质上,当我们设置了link选项,实际上是创建了一个postLink()链接函数,以便compile()函数可以定义链接函数。
通常情况下,如果设置了compile函数,说明我们希望在指令和实时数据被放到DOM中之前进行DOM操作,在这个函数中进行诸如添加和删除节点等DOM操作是安全的。
注:compile和link选项是互斥的。如果同时设置了这两个选项,那么会把compile所返回的函数当作链接函数,而link选项本身则会被忽略。
不要进行DOM事件监听器的注册:这个操作应该在链接函数中完成。
compile: function(tEle, tAttrs, transcludeFn) {
var tplEl = angular.element('<div>' +'<h2></h2>' +'</div>');
var h2 = tplEl.find('h2');
h2.attr('type', tAttrs.type);
h2.attr('ng-model', tAttrs.ngModel);
h2.val("hello");
tEle.replaceWith(tplEl);
return function(scope, ele, attrs) { // 连接函数 };}
编译函数负责对模板DOM进行转换。
链接函数负责将作用域和DOM进行链接。
3、链接
用link函数创建可以操作DOM的指令。
链接函数是可选的。如果定义了编译函数,它会返回链接函数,因此当两个函数都定义了时,编译函数会重载链接函数。如果我们的指令很简单,并且不需要额外的设置,可以从工厂函数 (回调函数)返回一个函数来代替对象。如果这样做了,这个函数就是链接函数。
**下面两种定义指令的方式在功能上是完全一样的:
**angular.module('myApp', [])
.directive('myDirective', function() {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被链接之前执行
// 在这里进行Don转换不安全
// 之后调用'lihk'h函数将无法定位要链接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被链接之后执行
// 如果在这里省略掉编译选项
//在这里执行DOM转换和链接函数一样安全吗
}
};
});
angular.module('myApp', [])
.directive('myDirective', function() {
return {
link: function(scope, ele, attrs) {
return {
pre: function(tElement, tAttrs, transclude) {
// 在子元素被链接之前执行
// 在这里进行Don转换不安全
// 之后调用'lihk'h函数将无法定位要链接的元素
},
post: function(scope, iElement, iAttrs, controller) {
// 在子元素被链接之后执行
// 如果在这里省略掉编译选项
//在这里执行DOM转换和链接函数一样安全吗
}
}
}
}
});
下面看一下链接函数中的参数:
scope
指令用来在其内部注册监听器的作用域。
iElement
iElement参数代表实例元素,指使用此指令的元素。在postLink函数中我们应该只操作此元素的子元素,因为子元素已经被链接过了。
iAttrs
iAttrs参数代表实例属性,是一个由定义在元素上的属性组成的标准化列表,可以在所有指令的链接函数间共享。会以javascript对象的形式进行传递。
controller
controller 参 数 指 向 require 选 项 定 义 的 控 制 器 。 如 果 没 有 设 置 require 选 项 , 那 么controller参数的值为undefined。
控制器在所有的指令间共享,因此指令可以将控制器当作通信通道(公共API)。如果设置了多个require,那么这个参数会是一个由控制器实例组成的数组,而不只是一个单独的控制器。