Angular JS:当我们已经有了具有作用域的指令控制器时,指令的link函数还需要什么?

我需要对作用域和模板执行一些操作。似乎我可以在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函数的意义是什么?

169377 次浏览

为什么需要控制器

当你想在DOM中嵌套指令并将API函数从父指令公开到嵌套指令时,linkcontroller之间的区别就开始发挥作用了。

文档:

最佳实践:当你想将API暴露给其他指令时使用controller。否则使用link。

假设你想要有两个指令my-formmy-text-input,你想要my-text-input指令只出现在my-form里面,而不在其他地方。

在这种情况下,在定义指令my-text-input时,你会说它使用require参数从parent DOM元素需要一个控制器,就像这样:require: '^myForm'。现在父元素的控制器将injected放入link函数中,作为$scope, element, attributes之后的第四个参数。你可以调用该控制器上的函数并与父指令通信。

此外,如果没有找到这样的控制器,则会引发错误。

为什么要使用link呢

如果定义了controller,则不需要使用link函数,因为$scopecontroller上可用。此外,在定义linkcontroller时,确实需要注意两者的调用顺序(controller在前面执行)。

然而,为了与角的方法保持一致,大多数使用$watchers的DOM操作和双向绑定通常在link函数中完成,而用于子函数和$scope操作的API则在controller中完成。这不是一个严格的规则,但这样做将使代码更加模块化,并有助于分离关注点(控制器将维护directive状态,link函数将维护DOM +外部绑定)。

controller函数/对象表示一个抽象模型-视图-控制器(MVC)。虽然关于MVC没有什么新鲜的东西可写,但它仍然是angular最重要的优势:将关注点分割成更小的部分。仅此而已,所以如果你需要对来自ViewModel变化做出反应,Controller是正确的来做这项工作。

关于link函数的故事是不同的,它来自不同的视角,然后MVC。并且是非常重要的,一旦我们想要跨越controller/model/view (模板)的边界。

让我们从传入link函数的参数开始:

function link(scope, element, attrs) {
  • 范围是一个Angular作用域对象。
  • 元素是这个指令匹配的jqlite包装元素。
  • attrs是一个具有规范化属性名称及其对应值的对象。

为了把link放到上下文中,我们应该提到所有指令都要经过这个初始化过程步骤:编译链接Brad Green和Shyam Seshadri合著的《Angular JS》的摘录:

编译阶段 (link的姐妹,让我们在这里提到它以获得清晰的图像):

在这个阶段,Angular遍历DOM以识别所有已注册的对象 模板中的指令。对于每个指令,它然后转换 DOM基于指令的规则(模板,替换,透射,和 等等),如果存在,则调用compile函数。结果是 编译模板函数,

链接阶段:

为了使视图动态,Angular会为每个视图运行一个链接函数 指令。链接函数通常在DOM上创建侦听器 或者模型。这些侦听器保持视图和模型在同步 。< / p >

一个如何使用link的好例子可以在这里找到:创建自定义指令。参见示例:创建一个操作DOM的指令,它将“date-time”插入到页面中,每秒刷新一次。

只是上面丰富的源代码中的一个非常短的片段,展示了DOM的实际操作。$timeout服务有一个挂钩函数,并且它在析构函数调用中被清除,以避免内存泄漏

.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);
});


...

在我的最初的linkcontroller函数斗争并阅读了大量关于它们的内容之后,我想现在我有了答案。

首先让理解

简单地说,angular指令是如何工作的:

  • 我们从模板开始(作为字符串或加载到字符串)

    var templateString = '<div my-directive>\{\{5 + 10}}</div>'; < / p >

  • 现在,这个templateString被包装为角元素

    var el = angular.element(templateString); < / p >

  • 使用el,现在我们用$compile编译它以获得链接函数。

    var l = $compile(el)

    事情是这样的,

    • $compile遍历整个模板并收集它所识别的所有指令。
    • 所有被发现的指令都是编制递归,并收集它们的link函数。
    • 然后,所有link函数都被包装在一个新的link函数中,并作为l返回。
    • 李< / ul > < / >
    • 最后,我们为这个l (link)函数提供了scope函数,该函数进一步执行带有scope及其对应元素的包装链接函数。

      l(scope) < / p >

    • 这将把template作为一个新节点添加到DOM中,并调用controller,后者将其手表添加到与DOM中的模板共享的作用域中。

    enter image description here

    比较编译 vs 链接 vs 控制器:

    • 每个指令只被编译一次,并且link函数被保留以供重用。因此,如果有一些东西适用于指令的所有实例,应该在directive的compile函数中执行。

    • 现在,在编译之后,我们有了link函数,该函数在将模板附加到DOM时执行。因此,我们执行的所有操作都是针对指令的每个实例的。例如:附加事件基于作用域突变模板,等等。

    • 最后,当指令在DOM上工作时(在附加之后),控制器意味着可用来活动和响应。因此:

      (1)在用link建立视图[V](即模板)之后。$scope是我们的[],而$controller是我们在M v c中的[C]

      (2)通过设置手表来利用双向美元的范围的绑定。

      (3) $scope手表预计将被添加到控制器中,因为这是在运行时监视模板的。

      (4)最后,controller也被用来在相关指令之间通信。(类似于https://docs.angularjs.org/guide/directive中的myTabs示例)

      (5)的确,我们也可以在link函数中完成这一切,但它是关于分离关注点.

    因此,最后我们得到了以下内容,它完美地符合所有部分:

    enter image description here