Angular JS:当我们已经有了指令的控制器和范围的时候,这个指令的链接函数需要什么?
我需要在范围和模板上执行一些操作。 看来我可以在link
函数或controller
函数中这样做(因为两者都可以访问范围)。
什么时候需要使用link
function而不是控制器?
angular.module('myApp').directive('abc', function($timeout) { return { restrict: 'EA', replace: true, transclude: true, scope: true, link: function(scope, elem, attr) { /* link function */ }, controller: function($scope, $element) { /* controller function */ } }; }
另外,我知道这个link
是无棱angular的世界。 所以,我可以使用$watch
, $digest
和$apply
。
当我们已经有了控制器的时候, link
函数的意义是什么?
在我最初的link
和controller
function的斗争和阅读了很多关于他们,我想现在我有答案。
首先让我们了解 ,
angular度指令如何工作:
-
我们从一个模板开始(作为一个string或者加载到一个string)
var templateString = '<div my-directive>{{5 + 10}}</div>';
-
现在,这个
templateString
被封装成一个angular度元素var el = angular.element(templateString);
-
用
el
,现在我们用$compile
编译它来获取链接函数。var l = $compile(el)
这是发生了什么,
-
$compile
遍历整个模板并收集它所识别的所有指令。 - 所有发现的指令都是recursion编译的,并且它们的
link
函数被收集。 - 然后,所有的
link
函数被包装在一个新的link
函数中,并返回为l
。
-
-
最后,我们为这个
l
(链接)函数提供scope
函数,进一步执行这个scope
及其相应元素的包装链接函数。l(scope)
-
这将
template
作为新节点添加到DOM
并调用controller
,将其手表添加到与DOM中的模板共享的范围 。
比较编译与链接 vs 控制器 :
-
每个指令只编译一次, 链接function保留重用。 因此,如果在指令的
compile
函数内部有适用于指令的所有实例的内容。 -
现在,在编译之后,我们有
link
function,它是在将模板附加到DOM时执行的 。 因此,我们执行每个指令的每个实例的具体内容。 例如: 附加事件 , 根据范围来变更模板等。 -
最后, 控制器意味着可以是生动的和被动的,而指令在
DOM
(在被连接之后)。 因此:(1)用链接build立视图[ V ](即模板)。
$scope
是我们的[ M ],$controller
是MVC中的 [ C ](2)通过设置手表来利用$ scope 的双向绑定。
(3)
$scope
手表预计将被添加到控制器中,因为这是在运行时观看模板的内容。(4)最后,
controller
也被用来在相关指令之间进行通信。 (像https://docs.angularjs.org/guide/directive中的;myTabs
示例)(5)的确,我们可以在
link
function中完成所有这些工作,但是关于问题的分离 。
因此,最后我们有以下几点完美地适合所有的部分:
为什么需要控制器
link
和controller
的区别在于你想要在你的DOM中嵌套指令,并且把父指令的API函数暴露给嵌套指令。
从文档 :
最佳实践:当您想要将API暴露给其他指令时,请使用控制器。 否则使用链接。
假设你想有两个指令my-form
和my-text-input
并且你希望my-text-input
指令只出现在my-form
和其他地方。
在这种情况下,您将在定义my-text-input
指令时说,它需要使用require
参数的parent
DOM元素的控制器,如下所示: require: '^myForm'
。 现在,来自父元素的控制器将作为第四个参数injected
link
函数,跟在$scope, element, attributes
。 您可以调用该控制器上的函数并与父指令进行通信。
而且,如果找不到这样的控制器,就会出现错误。
为什么要使用链接
由于$scope
在controller
上可用,因此如果定义了controller
则不需要使用link
function。 而且,在定义link
和controller
,还需要注意两者的调用顺序( controller
在之前执行)。
然而,按照Angular的方式 ,大多数使用$watchers
DOM操作和2-way绑定通常是在link
函数中完成的,而用于儿童的API和$scope
操作在controller
完成。 这不是一个硬性规则,但这样做会使代码更加模块化,并有助于分离关注点(控制器将保持directive
状态,并且link
function将维护DOM
+外部绑定)。
controller
function/对象表示抽象模型 – 视图 – 控制器(MVC)。 虽然没有什么新的MVC写,它仍然是angular的最重要的优势:将关注分成更小的块。 就是这样,所以如果您需要对来自“ View
” Controller
Model
更改做出反应,那么这是正确的人选 。
关于link
function的故事是不同的,它是从不同的angular度来看MVC。 一旦我们想要跨越controller/model/view
(模板)的界限,真的很重要。
让我们从传递给link
函数的参数开始:
function link(scope, element, attrs) {
- 范围是一个angular度范围对象。
- 元素是该指令匹配的jqLite-wrapped元素。
- attrs是具有标准化属性名称及其对应值的对象。
为了把link
放到上下文中,我们应该提到所有的指令都经历了这个初始化过程的步骤: 编译 , 链接 。 Brad Green和Shyam Seshadri的一本书Angular JS :
编译阶段 (链接的姐妹,让我们在这里提到一个清晰的图片):
在这个阶段,Angular走DOM去识别模板中的所有注册指令。 对于每个指令,然后根据指令的规则(模板,replace,transclude等等)转换DOM,并在编译函数存在的情况下调用它。 结果是编译模板函数,
链接阶段 :
为了使视图dynamic化,Angular为每个指令运行一个链接函数。 链接function通常在DOM或模型上创build侦听器。 这些监听器始终保持视图和模型同步。
如何使用link
一个很好的例子可以在这里find: 创build自定义指令 。 请参阅示例: 创build操纵DOM的指令,该指令在页面中插入“date – 时间”,每秒刷新一次。
从上面的丰富源代码只是一个非常短的片段,展示了真正的DOM操作。 有$ hook服务的挂钩函数,并且它的析构函数调用清除,以避免内存泄漏
.directive('myCurrentTime', function($timeout, dateFilter) { function link(scope, element, attrs) { ... // the not MVC job must be done function updateTime() { element.text(dateFilter(new Date(), format)); // here we are manipulating the DOM } function scheduleUpdate() { // save the timeoutId for canceling timeoutId = $timeout(function() { updateTime(); // update DOM scheduleUpdate(); // schedule the next update }, 1000); } element.on('$destroy', function() { $timeout.cancel(timeoutId); }); ...
我会在现有的答案中再补充一点。 链接function保证在DOM准备就绪后挂钩,这样在使用链接时可以考虑。