Angular JS:当我们已经有了具有作用域的指令控制器时,还需要指令的链接函数吗?
Posted
技术标签:
【中文标题】Angular JS:当我们已经有了具有作用域的指令控制器时,还需要指令的链接函数吗?【英文标题】:Angular JS: What is the need of the directive’s link function when we already had directive’s controller with scope? 【发布时间】:2013-11-29 20:59:54 【问题描述】:我需要对范围和模板执行一些操作。看来我可以在 link
函数或 controller
函数中做到这一点(因为两者都可以访问范围)。
什么时候我必须使用link
函数而不是控制器?
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
是非角世界。所以,我可以使用$watch
、$digest
和$apply
。
link
函数的意义是什么,当我们已经有了控制器的时候?
【问题讨论】:
你说的“是什么意思另外,我知道链接是非角世界。所以,我可以使用$watch
、$digest
和$apply
。" ?
在link
内部,我们看不到任何角度魔法。即没有 2 路绑定等。只是我们有 angular 的 api 可供使用。
【参考方案1】:
在我最初与link
和controller
函数斗争并阅读了很多关于它们的内容之后,我想现在我有了答案。
首先让我们了解,
简而言之,角度指令是如何工作的:
我们从一个模板开始(作为一个字符串或加载到一个字符串)
var templateString = '<div my-directive>5 + 10</div>';
现在,这个templateString
被包装成一个角元素
var el = angular.element(templateString);
使用el
,现在我们使用$compile
编译它以获取link函数。
var l = $compile(el)
这就是发生的事情,
$compile
遍历整个模板并收集它识别的所有指令。
发现的所有指令都递归编译,并收集了它们的link
函数。
然后,所有link
函数都包装在一个新的link
函数中,并返回为l
。
最后,我们为这个l
(链接)函数提供了scope
函数,该函数进一步使用这个scope
及其相应的元素执行包装的链接函数。
l(scope)
这会将template
作为新节点添加到DOM
并调用controller
,将其监视添加到与DOM 中的模板共享的范围。
比较 compile vs link vs controller:
每个指令只编译一次,link 函数被保留以供重复使用。因此,如果有适用于指令所有实例的东西,应该在指令的compile
函数中执行。
现在,在编译之后,我们有了link
函数,该函数在将 模板 附加到 DOM 时执行。因此,我们执行特定于指令每个实例的所有操作。例如:附加事件、根据范围改变模板等
最后,当指令在DOM
上工作时(附加后),控制器 可以实时响应。因此:
(1) 使用链接设置视图[V](即模板)后。 $scope
是我们的 [M],$controller
是我们在 M V C
(2) 通过设置监视来利用 2-way 与 $scope 的绑定。
(3) $scope
监视应该被添加到控制器中,因为这是在运行时监视模板的内容。
(4) 最后,controller
还用于能够在相关指令之间进行通信。 (如myTabs
中的https://docs.angularjs.org/guide/directive 示例)
(5) 确实,我们也可以在 link
函数中完成所有这些操作,但它是关于关注点分离。
因此,最后我们得到了完美契合所有部分的以下内容:
【讨论】:
我还发现这篇文章有助于理解这里的执行顺序:The nitty-gritty of compile and link functions inside AngularJS directives 很好的解释。想提一下,控制器在链接功能之前被调用。 控制器在链接之前执行 让我很生气的是 Stack Overflow 要求编辑至少有 6 个字符,因此不允许我在这个答案中更正 let's 的拼写。【参考方案2】:为什么需要控制器
link
和 controller
之间的区别在您想要在 DOM 中嵌套指令并将 API 函数从父指令公开给嵌套指令时发挥作用。
来自docs:
最佳实践:当您想将 API 公开给其他指令时,请使用控制器。否则使用链接。
假设您想要有两个指令 my-form
和 my-text-input
,并且您希望 my-text-input
指令仅出现在 my-form
中,而不会出现在其他任何地方。
在这种情况下,您会在定义指令 my-text-input
时说,它需要使用 require 参数来自 parent
DOM 元素的控制器,如下所示:require: '^myForm'
。现在来自父元素的控制器将injected
作为第四个参数进入link
函数,紧随$scope, element, attributes
。您可以调用该控制器上的函数并与父指令进行通信。
而且,如果没有找到这样的控制器,则会引发错误。
为什么要使用链接
如果定义controller
,则实际上不需要使用link
函数,因为$scope
在controller
上可用。此外,在同时定义link
和controller
时,确实需要注意两者的调用顺序(controller
在之前执行)。
然而,为了与 Angular 方式保持一致,大多数使用 $watchers
的 DOM 操作和 2 路绑定通常在 link
函数中完成,而用于子项的 API 和 $scope
操作在controller
中完成。这不是一个硬性规定,但这样做会使代码更加模块化并有助于分离关注点(控制器将保持directive
状态,link
函数将保持DOM
+ 外部绑定)。
【讨论】:
太好了。现在,你能帮我解决问题的第二部分吗? 你的回答并不能回答实际的问题。 我们在定义controller
的时候会出现什么问题吗?为什么我要发明一个全新的功能来避免定义控制器?
看来@scalaGirl 的链接已经失效了
我会尝试,但似乎我不能,除非我删除整个评论,所以在这里复制我的原始评论,然后从上面删除它。 “从实际的角度来看,您应该始终将您的 DOM 操作放在链接中,并将业务逻辑放在控制器中,这更像是角度约定。有些人会将这两者都放在链接中,这很好,但有点违反了分离关注点原则。底线是,将 DOM 操作和业务逻辑完全放在链接中比将它们完全放在控制器中“更安全”。”【参考方案3】:
controller
函数/对象表示抽象模型-视图-控制器 (MVC)。虽然关于 MVC 没有什么新东西可写,但它仍然是 Angular 最重要的优势:将关注点拆分成更小的部分。仅此而已,因此,如果您需要对来自View
的Model
更改做出反应,Controller
是完成这项工作的正确人。
link
函数的故事不同,它来自与 MVC 不同的角度。并且非常重要,一旦我们想要跨越controller/model/view
(template) 的边界。
让我们从传递给link
函数的参数开始:
function link(scope, element, attrs)
scope 是一个 Angular 范围对象。
element 是该指令匹配的 jqLite 包装元素。
attrs 是一个具有标准化属性名称及其对应值的对象。
要将link
放到上下文中,我们应该提到所有指令都经过这个初始化过程步骤:编译、链接。摘自 Brad Green 和 Shyam Seshadri 的 Angular JS 一书:
编译阶段(链接的妹子,这里提一下,看清楚):
在这个阶段,Angular 会遍历 DOM 来识别所有已注册的 模板中的指令。对于每个指令,它然后转换 DOM 基于指令的规则(模板、替换、转置和 以此类推),如果存在则调用编译函数。结果是一个 编译模板函数,
链接阶段:
为了使视图动态化,Angular 会为每个视图运行一个链接函数 指示。链接函数通常在 DOM 上创建侦听器 或模型。这些侦听器使视图和模型保持同步 任何时候。
可以在此处找到如何使用link
的一个很好的示例:Creating Custom Directives。请参阅示例:创建一个操作 DOM 的指令,它将“日期时间”插入页面,每秒刷新一次。
上面的 丰富 源代码中的一个非常短的 sn-p,显示了对 DOM 的真实操作。 $timeout 服务有钩子函数,并且在其 destructor 调用中清除它以避免内存泄漏
.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);
);
...
【讨论】:
您似乎比较了compiler
和link
。他们的问题是问为什么link
我们已经有了controller
我已经扩展了答案以更详细地描述控制器。现在controller
vs link
的概念应该更清楚了...
我可以接受这个解释。但那里似乎有点模糊。如果 Angular 团队本身的某个人可以为它说话,将他们看到的方向预测到 link
或 controller
,那就太好了。
这是我唯一想了解的部分(什么时候还不够?)。另外,我在controller
和link
中获得了角度的所有好处,相对丑陋。因此,Angular 团队必须有充分的理由,而不仅仅是一种选择。
问题:当Controller不够用? Ans: 当你需要 Angular 之外的体验时,例如使用 JQuery 插件或使用文档中提到的 JQlite 功能(docs.angularjs.org/api/ng/function/angular.element:),那么你需要链接以上是关于Angular JS:当我们已经有了具有作用域的指令控制器时,还需要指令的链接函数吗?的主要内容,如果未能解决你的问题,请参考以下文章