链接vs编译vs控制器


529

创建指令时,可以将代码放入编译器,链接函数或控制器中。

在文档中,他们解释说:

  • 编译和链接功能用于角度循环的不同阶段
  • 指令之间共享控制器

但是,对我来说,不清楚哪种代码应该放在哪里。

例如:我可以在编译中创建函数并将它们附加到链接的作用域中,还是仅将函数附加到控制器的作用域中?

如果每个指令可以有自己的控制器,则如何在指令之间共享控制器?控制器是真正共享的还是仅仅是作用域属性?




1
我写了一篇关于指令生命周期图(创建阶段)的文章。也许对某人有帮助:filimanjaro.com/2014/…– 2014
平均乔

Answers:


470

编译:

这是Angular实际编译指令的阶段。对于给定指令的每个引用,仅一次调用此编译函数。例如,假设您正在使用ng-repeat指令。ng-repeat将必须查找其附加到的元素,提取其附加到的html片段,并创建一个模板函数。

如果您使用过HandleBars,下划线模板或等效模板,则就像编译其模板以提取出模板函数一样。向此模板函数传递数据,该函数的返回值是html,数据放在正确的位置。

编译阶段是Angular中返回模板功能的那一步。这种以角度表示的模板函数称为链接函数。

链接阶段:

链接阶段是将数据($ scope)附加到链接函数的阶段,它应该返回链接的html。由于该指令还指定了该html的位置或更改的位置,因此可以很好地使用它。这是您要更改链接的html的功能,即已附加数据的html。如果您在链接功能中编写代码,则为角度形式,通常是后链接功能(默认情况下)。这是一种在链接功能将数据与模板链接之后被调用的回调。

控制器:

控制器是放置特定指令逻辑的地方。该逻辑也可以进入链接功能,但是您必须将该逻辑放在作用域上才能使其“可共享”。这样做的问题是,您随后将使用指令内容破坏范围,这实际上并不是预期的。那么,如果两个指令要互相交谈/彼此合作,该怎么办?当然,您可以将所有逻辑放入服务中,然后使这两个指令都依赖于该服务,但这只会带来一个依赖性。另一种方法是为此范围提供一个Controller(通常是隔离范围?),然后当该控制器“需要”另一个指令时,将该控制器注入另一个指令。


67
需要说明的是:compile编译要在整个页面中使用的模板。链接器绑定到每个实例。对?然后,控制器将在实例之间工作。
Zlatko 2014年

4
@CMCDragonkai每个指令controller功能执行编译,但之前 pre-link在当地的DOM树的分支。并且controllerpre-link自上而下的方式遍历本地DOM分支并执行功能。之后,post-link自下而上的方式执行。
Artem Platonov 2014年

9
如果您不了解,那只会是一团糟。它有理由做它做的事情。
demisx

3
这是正确的技术答案,但是,对于何时应该使用链接功能,我仍然有疑问。
尼古拉斯·马歇尔

2
我们应该使用controller而不是link到处使用吗?这样,如果该方法需要共享或需要引入一些逻辑,那么以后就不需要更改代码了。一直使用controller链接而不是链接有什么陷阱吗?
JPS 2015年

99

我还想补充一下Google团队写的O'Reily AngularJS书中所说的话:

控制器-创建一个控制器,该控制器发布用于跨指令通信的API。一个很好的例子是指令通信指令

链接-以编程方式修改生成的DOM元素实例,添加事件侦听器并设置数据绑定。

编译-像在ng-repeat中使用时一样,以编程方式修改跨指令副本的功能的DOM模板。您的编译函数还可以返回链接函数,以修改生成的元素实例。


您的thinkster.io链接必须付费才能观看。不是我的链接,但也许更合适:toddmotto.com/directive-to-directive-communication-with-require
R. van

51

A directive允许您以声明的方式扩展HTML词汇表以构建Web组件。该ng-app属性是一个指令,ng-controller所有也是ng- prefixed attributes。指令可以attributestags甚至class namescomments

指令的诞生方式(compilationinstantiation

编译:我们将在DOM呈现之前将compile函数用于manipulateDOM并返回一个link函数(该函数将为我们处理链接)。这也是放置所有需要与instances该指令的所有方法共享的方法的地方。

链接:我们将使用该link函数在特定DOM元素(从模板中克隆)上注册所有侦听器,并设置与页面的绑定。

如果在compile()函数中设置它们,它们将只会被设置一次(通常是您想要的)。如果在link()函数中设置,则每次将HTML元素绑定到 对象中的数据时都将设置它们。

<div ng-repeat="i in [0,1,2]">
    <simple>
        <div>Inner content</div>
    </simple>
</div>

app.directive("simple", function(){
   return {
     restrict: "EA",
     transclude:true,
     template:"<div>{{label}}<div ng-transclude></div></div>",        
     compile: function(element, attributes){  
     return {
             pre: function(scope, element, attributes, controller, transcludeFn){

             },
             post: function(scope, element, attributes, controller, transcludeFn){

             }
         }
     },
     controller: function($scope){

     }
   };
});

Compile函数返回prepost链接函数。在pre link函数中,我们具有实例模板以及的作用域controller,但是该模板未绑定到作用域,并且仍然没有包含的内容。

Post链接功能是发布链接是最后执行的功能。现在,transclusion完成了the template is linked to a scope,并且view will update with data bound values after the next digest cycle。该link选项只是设置post-link功能的快捷方式。

controller:指令控制器可以传递到另一个指令链接/编译阶段。它可以注入到其他指令中,作为在指令间通信中使用的一种手段。

您必须指定所需的指令名称-它应该绑定到相同的元素或其父元素。该名称可以带有以下前缀:

?  Will not raise any error if a mentioned directive does not exist.
^  Will look for the directive on parent elements, if not available on the same element.

使用方括号[‘directive1′, ‘directive2′, ‘directive3′]需要多个指令控制器。

var app = angular.module('app', []);

app.controller('MainCtrl', function($scope, $element) {
});

app.directive('parentDirective', function() {
  return {
    restrict: 'E',
    template: '<child-directive></child-directive>',
    controller: function($scope, $element){
      this.variable = "Hi Vinothbabu"
    }
  }
});

app.directive('childDirective', function() {
  return {
    restrict:  'E',
    template: '<h1>I am child</h1>',
    replace: true,
    require: '^parentDirective',
    link: function($scope, $element, attr, parentDirectCtrl){
      //you now have access to parentDirectCtrl.variable
    }
  }
});

1
您提到过,您展示了如何将parentDirectiveCtrl放入孩子的控制器中……这个例子中孩子没有控制器,而是链接功能……我目前还没有遇到这个问题,所以可能不是如此重要,但却是一个奇怪的问题。
alockwood05年

13

另外,使用控制器vs.链接功能的一个很好的理由(因为它们都可以访问作用域,元素和属性)是因为您可以将任何可用的服务或依赖项(按任何顺序)传递给控制器​​,而您无法使用链接功能做到这一点。请注意不同的签名:

controller: function($scope, $exceptionHandler, $attr, $element, $parse, $myOtherService, someCrazyDependency) {...

link: function(scope, element, attrs) {... //no services allowed

2
当您对答案不满意时,请发表评论以解释您的观点。谢谢
svassr 2013年

53
我不是拒绝投票的人,但这并不是完全正确的,因为您仍然可以将任何必需的依赖项注入指令本身,例如:module.directive('myDirective', function($window) { etc...。然后可以从链接功能内部进行访问。
Mike Chamberlain

1
这似乎直截了当是不正确的,因为您可以将服务注入链接功能中
Code Whisperer 2014年

1
@JoshRibakoff最终结果是相同的,您可以在链接功能中访问该服务。是否在函数的参数中声明无关紧要。在这方面,迈克·张伯伦是正确的
康纳·怀亚特

1
@ cwyatt1我正在纠正说法,plnkr没有显示注入到link()函数中,因为那不是Angular所具有的功能。您可能会以为我很书呆子,但是metamatts评论已经概述了plunkr和注入控制器之间的许多重要区别。OP正在询问有什么区别,并且存在区别。
Josh Ribakoff 2015年

10

这是了解指令阶段的好示例 http://codepen.io/anon/pen/oXMdBQ?editors=101

var app = angular.module('myapp', [])

app.directive('slngStylePrelink', function() {
    return {
        scope: {
            drctvName: '@'
        },
        controller: function($scope) {
            console.log('controller for ', $scope.drctvName);
        },
        compile: function(element, attr) {
            console.log("compile for ", attr.name)
            return {
                post: function($scope, element, attr) {
                    console.log('post link for ', attr.name)
                },
                pre: function($scope, element, attr) {
                    $scope.element = element;
                    console.log('pre link for ', attr.name)
                        // from angular.js 1.4.1
                    function ngStyleWatchAction(newStyles, oldStyles) {
                        if (oldStyles && (newStyles !== oldStyles)) {
                            forEach(oldStyles, function(val, style) {
                                element.css(style, '');
                            });
                        }
                        if (newStyles) element.css(newStyles);
                    }

                    $scope.$watch(attr.slngStylePrelink, ngStyleWatchAction, true);

                    // Run immediately, because the watcher's first run is async
                    ngStyleWatchAction($scope.$eval(attr.slngStylePrelink));
                }
            };
        }
    };
});

html

<body ng-app="myapp">
    <div slng-style-prelink="{height:'500px'}" drctv-name='parent' style="border:1px solid" name="parent">
        <div slng-style-prelink="{height:'50%'}" drctv-name='child' style="border:1px solid red" name='child'>
        </div>
    </div>
</body>

4
你能解释为何此示例代码将有助于理解之间的区别linkcompile以及controller
cel锐

您知道如何将required指令注入到从属指令的控制器中吗?
alockwood05年

您的代码笔示例:未捕获的错误:[$ injector:modulerr]由于以下原因而无法实例化模块myapp:错误:[$ injector:unpr]未知提供程序:slngStylePrelinkProvider
rofrol

7
  • compile:当我们需要修改指令模板时使用,例如添加新表达式,在该指令内附加另一个指令
  • controller:当我们需要共享/重用$ scope数据时使用
  • link:当我们需要附加事件处理程序或操纵DOM时使用的函数。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.