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】:

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

首先让我们了解

简而言之,角度指令是如何工作的:

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

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

中的 [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】:

为什么需要控制器

linkcontroller 之间的区别在您想要在 DOM 中嵌套指令并将 API 函数从父指令公开给嵌套指令时发挥作用。

来自docs:

最佳实践:当您想将 API 公开给其他指令时,请使用控制器。否则使用链接。

假设您想要有两个指令 my-formmy-text-input,并且您希望 my-text-input 指令仅出现在 my-form 中,而不会出现在其他任何地方。

在这种情况下,您会在定义指令 my-text-input 时说,它需要使用 require 参数来自 parent DOM 元素的控制器,如下所示:require: '^myForm'。现在来自父元素的控制器将injected 作为第四个参数进入link 函数,紧随$scope, element, attributes。您可以调用该控制器上的函数并与父指令进行通信。

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

为什么要使用链接

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

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

【讨论】:

太好了。现在,你能帮我解决问题的第二部分吗? 你的回答并不能回答实际的问题。 我们在定义controller的时候会出现什么问题吗?为什么我要发明一个全新的功能来避免定义控制器? 看来@scalaGirl 的链接已经失效了 我会尝试,但似乎我不能,除非我删除整个评论,所以在这里复制我的原始评论,然后从上面删除它。 “从实际的角度来看,您应该始终将您的 DOM 操作放在链接中,并将业务逻辑放在控制器中,这更像是角度约定。有些人会将这两者都放在链接中,这很好,但有点违反了分离关注点原则。底线是,将 DOM 操作和业务逻辑完全放在链接中比将它们完全放在控制器中“更安全”。”【参考方案3】:

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

link 函数的故事不同,它来自与 MVC 不同的角度。并且非常重要,一旦我们想要跨越controller/model/view (template) 的边界。

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

function link(scope, element, attrs) 
scope 是一个 Angular 范围对象。 element 是该指令匹配的 jqLit​​e 包装元素。 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);
 );

 ...

【讨论】:

您似乎比较了compilerlink。他们的问题是问为什么link 我们已经有了controller 我已经扩展了答案以更详细地描述控制器。现在controller vs link 的概念应该更清楚了... 我可以接受这个解释。但那里似乎有点模糊。如果 Angular 团队本身的某个人可以为它说话,将他们看到的方向预测到 linkcontroller,那就太好了。 这是我唯一想了解的部分(什么时候还不够?)。另外,我在controllerlink 中获得了角度的所有好处,相对丑陋。因此,Angular 团队必须有充分的理由,而不仅仅是一种选择。 问题:当Controller不够用? Ans: 当你需要 Angular 之外的体验时,例如使用 JQuery 插件或使用文档中提到的 JQlite 功能(docs.angularjs.org/api/ng/function/angular.element:),那么你需要链接

以上是关于Angular JS:当我们已经有了具有作用域的指令控制器时,还需要指令的链接函数吗?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 11,js在角度路由后不起作用

js作用域的相关知识

java python js作用域的对比

js作用域的销毁不立即销毁不销毁

让 holder.js 与 Angular 一起工作

当表单出现错误时,带有 angular.js 表单提交的 Django 不起作用