何时以及如何使用compile,controller,pre
在编写Angular指令时,可以使用以下任何函数来操作DOM行为,声明指令的元素的内容和外观:
对于应该使用哪种功能,似乎存在一些混淆。 这个问题包括:
指令基础
功能性,do和dont's
相关问题:
指令函数以何种顺序执行?
对于单个指令
基于下面的plunk,请考虑以下HTML标记:
<body>
<div log='some-div'></div>
</body>
通过以下指令声明:
myApp.directive('log', function() {
return {
controller: function( $scope, $element, $attrs, $transclude ) {
console.log( $attrs.log + ' (controller)' );
},
compile: function compile( tElement, tAttributes ) {
console.log( tAttributes.log + ' (compile)' );
return {
pre: function preLink( scope, element, attributes ) {
console.log( attributes.log + ' (pre-link)' );
},
post: function postLink( scope, element, attributes ) {
console.log( attributes.log + ' (post-link)' );
}
};
}
};
});
控制台输出将是:
some-div (compile)
some-div (controller)
some-div (pre-link)
some-div (post-link)
我们可以看到, compile
首先执行,然后是controller
,然后是pre-link
,最后是post-link
。
对于嵌套指令
注意:以下内容不适用于呈现其子项链接功能的指令。 不少角指示这样做(如ngIf,ngRepeat,或与任何指令transclude
)。 这些指令本来就会在它们的子指令compile
被调用之前调用它们的link
函数。
原始的HTML标记通常由嵌套元素组成,每个嵌套元素都有自己的指令。 就像下面的标记一样(参见plunk):
<body>
<div log='parent'>
<div log='..first-child'></div>
<div log='..second-child'></div>
</div>
</body>
控制台输出将如下所示:
// The compile phase
parent (compile)
..first-child (compile)
..second-child (compile)
// The link phase
parent (controller)
parent (pre-link)
..first-child (controller)
..first-child (pre-link)
..first-child (post-link)
..second-child (controller)
..second-child (pre-link)
..second-child (post-link)
parent (post-link)
我们可以在这里区分两个阶段 - 编译阶段和链接阶段。
编译阶段
当DOM被加载时,Angular开始编译阶段,它从上到下遍历标记,并调用所有指令的compile
。 在图形上,我们可以这样表达:
可能很重要的一点是,在这个阶段,编译功能获取的模板是源模板(不是实例模板)。
链接阶段
DOM实例通常只是源模板呈现给DOM的结果,但它们可能是由ng-repeat
创建的,或者是实时引入的。
每当具有指令的元素的新实例呈现给DOM时,链接阶段就开始。
在这个阶段,Angular调用controller
, pre-link
,迭代子代,并在所有指令中调用post-link
,如下所示:
这些函数调用之间还会发生什么?
各种指令函数是在两个其他角度函数中执行的,这两个函数称为$compile
(其中指令的compile
被执行)和一个名为nodeLinkFn
(指令的controller
, preLink
和postLink
被执行)的内部函数。 在调用指令函数之前和之后,角函数内会发生各种各样的事情。 也许最值得注意的是孩子递归。 下面的简图说明了编译和链接阶段的关键步骤:
为了演示这些步骤,我们使用下面的HTML标记:
<div ng-repeat="i in [0,1,2]">
<my-element>
<div>Inner content</div>
</my-element>
</div>
遵循以下指令:
myApp.directive( 'myElement', function() {
return {
restrict: 'EA',
transclude: true,
template: '<div>{{label}}<div ng-transclude></div></div>'
}
});
编
compile
API看起来像这样:
compile: function compile( tElement, tAttributes ) { ... }
通常这些参数前缀为t
来表示所提供的元素和属性是源模板的属性,而不是实例的属性。
在调用compile
transcluded内容(如果有的话)之前,将模板应用于标记。 因此,提供给compile
函数的元素将如下所示:
<my-element>
<div>
"{{label}}"
<div ng-transclude></div>
</div>
</my-element>
请注意,此处转录的内容不会重新插入。
在对指令的.compile
进行调用之后,Angular将遍历所有子元素,包括可能刚刚由指令引入的那些元素(例如模板元素)。
实例创建
在我们的例子中,将创建上面三个源模板实例(通过ng-repeat
)。 因此,下面的序列将执行三次,每个实例一次。
调节器
controller
API涉及:
controller: function( $scope, $element, $attrs, $transclude ) { ... }
进入链接阶段后,通过$compile
返回的链接函数现在提供了一个范围。
首先,链接函数根据请求创建一个子范围( scope: true
)或一个隔离范围( scope: {...}
)。
然后执行控制器,提供实例元素的作用域。
预链接
pre-link
API看起来像这样:
function preLink( scope, element, attributes, controller ) { ... }
事实上,没有任何反应的调用指令的之间.controller
和.preLink
功能。 Angular还提供了如何使用每个应用程序的建议。
在.preLink
调用之后,链接函数将遍历每个子元素 - 调用正确的链接函数并将其附加到当前作用域(作为子元素的父作用域)。
帖子链接
post-link
API与pre-link
功能类似:
function postLink( scope, element, attributes, controller ) { ... }
也许值得注意的是,一旦一个指令的.postLink
函数被调用,其所有子元素的链接过程就完成了,包括所有孩子的.postLink
函数。
这意味着在.postLink
被调用的时候,孩子们'活着'已经准备好了。 这包括:
这个阶段的模板将如下所示:
<my-element>
<div class="ng-binding">
"{{label}}"
<div ng-transclude>
<div class="ng-scope">Inner content</div>
</div>
</div>
</my-element>
如何声明各种功能?
编译,控制器,预链接和后链接
如果要使用全部四个函数,指令将遵循以下形式:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return {
pre: function preLink( scope, element, attributes, controller, transcludeFn ) {
// Pre-link code goes here
},
post: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
}
};
});
请注意,compile会返回一个包含前链接和后链接功能的对象; 在Angular术语中,我们说编译函数返回一个模板函数。
编译,控制器和后链接
如果不需要pre-link
,那么编译函数可以简单地返回后链接函数而不是定义对象,如下所示:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
};
}
};
});
有时候,希望在定义(后) link
方法之后添加一个compile
方法。 为此,可以使用:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
compile: function compile( tElement, tAttributes, transcludeFn ) {
// Compile code goes here.
return this.link;
},
link: function( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
}
};
});
控制器和后链接
如果不需要编译函数,则可以完全跳过它的声明并在指令的配置对象的link
属性下提供后链接功能:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
controller: function( $scope, $element, $attrs, $transclude ) {
// Controller code goes here.
},
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
没有控制器
在上述任何一个示例中,如果不需要,可以简单地删除controller
功能。 因此,例如,如果仅需要post-link
功能,则可以使用:
myApp.directive( 'myDirective', function () {
return {
restrict: 'EA',
link: function postLink( scope, element, attributes, controller, transcludeFn ) {
// Post-link code goes here
},
};
});
链接地址: http://www.djcxy.com/p/77905.html
上一篇: when and how to use compile, controller, pre
下一篇: Difference between the 'controller', 'link' and 'compile' functions when defining a directive