简体   繁体   English

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

[英]Angular JS: What is the need of the directive’s link function when we already had directive’s controller with scope?

I need to perform some operations on scope and the template.我需要对范围和模板执行一些操作。 It seems that I can do that in either the link function or the controller function (since both have access to the scope).似乎我可以在link函数或controller函数中做到这一点(因为两者都可以访问范围)。

When is it the case when I have to use link function and not the 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 */ }
    };
}

Also, I understand that link is the non-angular world.另外,我知道link是非角度世界。 So, I can use $watch , $digest and $apply .所以,我可以使用$watch$digest$apply

What is the significance of the link function, when we already had controller?当我们已经有了控制器时, link功能的意义是什么?

After my initial struggle with the link and controller functions and reading quite a lot about them, I think now I have the answer.在我对linkcontroller功能的最初挣扎并阅读了很多关于它们的内容之后,我想现在我有了答案。

First lets understand ,首先让我们了解一下

How do angular directives work in a nutshell:简而言之,角度指令是如何工作的:

  • We begin with a template (as a string or loaded to a string)我们从模板开始(作为字符串或加载到字符串)

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

  • Now, this templateString is wrapped as an angular element现在,这个templateString被包装成一个角度元素

    var el = angular.element(templateString);

  • With el , now we compile it with $compile to get back the link function.使用el ,现在我们用$compile编译它以取回链接函数。

    var l = $compile(el)

    Here is what happens,这是发生的事情,

    • $compile walks through the whole template and collects all the directives that it recognizes. $compile遍历整个模板并收集它识别的所有指令。
    • All the directives that are discovered are compiled recursively and their link functions are collected.所有发现的指令都被递归编译并收集它们的link函数。
    • Then, all the link functions are wrapped in a new link function and returned as l .然后,所有link函数都包装在一个新的link函数中并作为l返回。
  • Finally, we provide scope function to this l (link) function which further executes the wrapped link functions with this scope and their corresponding elements.最后,我们为这个l (链接)函数提供scope函数,该函数进一步执行具有此scope及其相应元素的包装链接函数。

    l(scope)

  • This adds the template as a new node to the DOM and invokes controller which adds its watches to the scope which is shared with the template in DOM.这会将template作为新节点添加到DOM并调用controllercontroller将其监视添加到与 DOM 中的模板共享的范围

在此处输入图片说明

Comparing compile vs link vs controller :比较编译vs链接vs控制器

  • Every directive is compiled only once and link function is retained for re-use.每个指令只编译一次,链接函数被保留以供重用。 Therefore, if there's something applicable to all instances of a directive should be performed inside directive's compile function.因此,如果有一些适用于指令的所有实例的东西应该在指令的compile函数中执行。

  • Now, after compilation we have link function which is executed while attaching the template to the DOM .现在,编译后我们有link函数,该函数在将模板附加到DOM 时执行 So, therefore we perform everything that is specific to every instance of the directive.因此,我们执行针对指令的每个实例的所有操作。 For eg: attaching events , mutating the template based on scope , etc.例如:附加事件基于范围改变模板等。

  • Finally, the controller is meant to be available to be live and reactive while the directive works on the DOM (after getting attached).最后,当指令在DOM (附加后),控制器是可用的。 Therefore:因此:

    (1) After setting up the view[ V ] (ie template) with link. (1) 设置好带有链接的视图[ V ](即模板)后。 $scope is our [ M ] and $controller is our [ C ] in MVC $scope是我们的 [ M ] 而$controller是我们在MVC 中的[ C ]

    (2) Take advantage the 2-way binding with $scope by setting up watches. (2) 通过设置监视来利用$scope2 向绑定。

    (3) $scope watches are expected to be added in the controller since this is what is watching the template during run-time. (3) $scope watch 预计会被添加到控制器中,因为这是在运行时监视模板的内容。

    (4) Finally, controller is also used to be able to communicate among related directives. (4) 最后, controller还用于能够在相关指令之间进行通信。 (Like myTabs example in https://docs.angularjs.org/guide/directive ) (就像https://docs.angularjs.org/guide/directive 中的myTabs示例)

    (5) It's true that we could've done all this in the link function as well but its about separation of concerns . (5) 确实,我们也可以在link函数中完成所有这些,但它是关于关注点分离的

Therefore, finally we have the following which fits all the pieces perfectly :因此,最后我们有以下完美适合所有部分的内容:

在此处输入图片说明

Why controllers are needed为什么需要控制器

The difference between link and controller comes into play when you want to nest directives in your DOM and expose API functions from the parent directive to the nested ones.当您想在 DOM 中嵌套指令并将 API 函数从父指令暴露给嵌套指令时, linkcontroller之间的区别就开始发挥作用了。

From the docs :文档

Best Practice: use controller when you want to expose an API to other directives.最佳实践:当您想向其他指令公开 API 时使用控制器。 Otherwise use link.否则使用链接。

Say you want to have two directives my-form and my-text-input and you want my-text-input directive to appear only inside my-form and nowhere else.假设您希望有两个指令my-formmy-text-input并且您希望my-text-input指令仅出现在my-form而不出现在其他任何地方。

In that case, you will say while defining the directive my-text-input that it requires a controller from the parent DOM element using the require argument, like this: require: '^myForm' .在这种情况下,您将在定义指令my-text-input使用 require 参数说它需要来自parent DOM 元素的控制器,例如: require: '^myForm' Now the controller from the parent element will be injected into the link function as the fourth argument, following $scope, element, attributes .现在来自父元素的控制器将作为第四个参数injectedlink函数中,紧跟在$scope, element, attributes You can call functions on that controller and communicate with the parent directive.您可以调用该控制器上的函数并与父指令进行通信。

Moreover, if such a controller is not found, an error will be raised.此外,如果找不到这样的控制器,则会引发错误。

Why use link at all为什么要使用链接

There is no real need to use the link function if one is defining the controller since the $scope is available on the controller .如果定义controller则没有真正需要使用link功能,因为$scopecontroller上可用。 Moreover, while defining both link and controller , one does need to be careful about the order of invocation of the two ( controller is executed before).此外,在定义linkcontroller ,确实需要注意两者的调用顺序( controller是在之前执行的)。

However, in keeping with the Angular way , most DOM manipulation and 2-way binding using $watchers is usually done in the link function while the API for children and $scope manipulation is done in the controller .然而,为了与Angular 的方式保持一致,大多数使用$watchers DOM 操作和 2 路绑定通常在link函数中完成,而子 API 和$scope操作在controller完成。 This is not a hard and fast rule, but doing so will make the code more modular and help in separation of concerns (controller will maintain the directive state and link function will maintain the DOM + outside bindings).这不是一个硬性规定,但这样做会使代码更加模块化并有助于分离关注点(控制器将维护directive状态, link函数将维护DOM + 外部绑定)。

The controller function/object represents an abstraction model-view-controller (MVC). controller函数/对象表示抽象模型-视图-控制器 (MVC)。 While there is nothing new to write about MVC, it is still the most significant advanatage of angular: split the concerns into smaller pieces.虽然关于 MVC 没有什么可写的,但它仍然是 angular 最重要的优点:将关注点分成更小的部分。 And that's it, nothing more, so if you need to react on Model changes coming from View the Controller is the right person to do that job.就是这样,仅此而已,因此,如果您需要对来自ViewModel更改做出反应,则Controller是完成这项工作的合适人选

The story about link function is different, it is coming from different perspective then MVC.关于link功能的故事是不同的,它来自与 MVC 不同的角度。 And is really essential, once we want to cross the boundaries of a controller/model/view (template) .并且非常重要,一旦我们想要跨越controller/model/view (模板)的边界。

Let' start with the parameters which are passed into the link function:让我们从传递给link函数的参数开始:

function link(scope, element, attrs) {
  • scope is an Angular scope object. scope是一个 Angular 作用域对象。
  • element is the jqLite-wrapped element that this directive matches. element是该指令匹配的 jqLit​​e 包装元素。
  • attrs is an object with the normalized attribute names and their corresponding values. attrs是一个具有规范化属性名称及其对应值的对象。

To put the link into the context, we should mention that all directives are going through this initialization process steps: Compile , Link .为了将link放入上下文中,我们应该提到所有指令都经过这个初始化过程步骤: CompileLink An Extract from Brad Green and Shyam Seshadri book Angular JS :摘自Brad Green 和 Shyam Seshadri 的书 Angular JS

Compile phase (a sister of link, let's mention it here to get a clear picture):编译阶段(链接的姐妹,这里提一下,看清楚):

In this phase, Angular walks the DOM to identify all the registered directives in the template.在这个阶段,Angular 遍历 DOM 以识别模板中所有已注册的指令。 For each directive, it then transforms the DOM based on the directive's rules (template, replace, transclude, and so on), and calls the compile function if it exists.对于每个指令,它然后根据指令的规则(模板、替换、转换等)转换 DOM,如果存在则调用 compile 函数。 The result is a compiled template function,结果是一个编译好的模板函数,

Link phase :链接阶段

To make the view dynamic, Angular then runs a link function for each directive.为了使视图动态化,Angular 会为每个指令运行一个链接函数。 The link functions typically creates listeners on the DOM or the model.链接函数通常在 DOM 或模型上创建侦听器。 These listeners keep the view and the model in sync at all times.这些侦听器始终保持视图和模型同步。

A nice example how to use the link could be found here: Creating Custom Directives .可以在此处找到如何使用该link一个很好的示例:创建自定义指令 See the example: Creating a Directive that Manipulates the DOM , which inserts a "date-time" into page, refreshed every second.请参阅示例:创建操作 DOM 的指令,该指令将“日期时间”插入页面,每秒刷新一次。

Just a very short snippet from that rich source above, showing the real manipulation with DOM.只是来自上面丰富资源的一个非常短的片段,显示了对 DOM 的真实操作。 There is hooked function to $timeout service, and also it is cleared in its destructor call to avoid memory leaks $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);
 });

 ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM