链接 vs 编译 vs 控制器

Posted

技术标签:

【中文标题】链接 vs 编译 vs 控制器【英文标题】:Link vs compile vs controller 【发布时间】:2013-03-18 13:58:59 【问题描述】:

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

在文档中,他们解释说:

编译和链接函数用于angular的不同阶段 循环 控制器在指令之间共享

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

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

如果每个指令都可以有自己的控制器,那么指令之间如何共享控制器?控制器真的是共享的还是只是范围属性?

【问题讨论】:

另见***.com/questions/12546945/… 或许更全面的指令函数概述:Angular directives - when to use compile, controller, pre-link and post-link. 我写了一篇带有指令生命周期图(创建阶段)的帖子。也许它可以帮助某人:filimanjaro.com/2014/… Difference between the 'controller', 'link' and 'compile' functions when defining a directive的可能重复 【参考方案1】: compile:当我们需要修改指令模板时使用,例如添加新表达式,在该指令中附加另一个指令 控制器:当我们需要共享/重用 $scope 数据时使用 link:它是我们需要附加事件处理程序或操作 DOM 时使用的函数。

【讨论】:

【参考方案2】:

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

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

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

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

【讨论】:

您的 thinkster.io 链接不付费就无法观看。不是我的链接,但也许这更合适:toddmotto.com/directive-to-directive-communication-with-require【参考方案3】:

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

指令是如何产生的(compilationinstantiation

编译:我们将在渲染之前将compile 函数用于manipulate DOM,并返回一个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 链接函数。在预链接函数中,我们有实例模板和来自controller 的范围,但是模板没有绑定到范围并且仍然没有嵌入的内容。

Post 链接函数是 post 链接是最后执行的函数。现在transclusion 已完成,the template is linked to a scopeview will update with data bound values after the next digest cyclelink 选项只是设置post-link 函数的快捷方式。

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

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

? – 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
    
  
);

【讨论】:

你提到你展示了如何让 parentDirectiveCtrl 进入孩子的控制器......这个例子孩子没有控制器,而是链接功能......我没有坚持这个目前的问题,所以它可能不是那么重要,而是一个奇怪的问题。【参考方案4】:

这是理解指令阶段的一个很好的示例 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>

【讨论】:

您能否详细说明为什么此示例代码有助于理解linkcompilecontroller 之间的区别? 你知道如何将required 指令注入到依赖指令的控制器中吗? 您的代码示例:未捕获的错误:[$injector:modulerr] 无法实例化模块 myapp 由于:错误:[$injector:unpr] 未知的提供程序:slngStylePrelinkProvider【参考方案5】:

编译:

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

如果您使用过 HandleBars、下划线模板或类似的模板,就像编译它们的模板以提取模板函数一样。向此模板函数传递数据,该函数的返回值是包含正确位置数据的 html。

编译阶段是 Angular 中返回模板函数的那一步。这个 Angular 中的模板函数称为链接函数。

链接阶段:

链接阶段是将数据 ( $scope ) 附加到链接函数的地方,它应该返回链接的 html。由于该指令还指定了此 html 的去向或更改的内容,因此它已经很好了。这是您要更改链接的 html 的函数,即已经附加了数据的 html。如果您在链接函数中编写代码,则它通常是链接后函数(默认情况下)。这是一种在链接函数将数据与模板链接后调用的回调。

控制器:

控制器是您放置某些指令特定逻辑的地方。该逻辑也可以进入链接功能,但是您必须将该逻辑放在范围内以使其“可共享”。这样做的问题是你会用你的指令破坏范围,这并不是真正的预期。 那么,如果两个指令想要相互交谈/相互合作,还有什么选择呢?当然,您可以将所有这些逻辑放入服务中,然后使这两个指令都依赖于该服务,但这只会带来更多的依赖。另一种方法是为此范围提供一个控制器(通常是隔离范围?),然后当该指令“需要”另一个指令时,将此控制器注入另一个指令。有关示例,请参见 angularjs.org 第一页上的选项卡和窗格。

【讨论】:

澄清一下:compile 编译整个页面使用的模板。链接器与每个实例相关联。正确的?然后控制器在实例之间工作。 @CMCDragonkai 每个指令controller 函数在编译之后 执行,但之前 pre-link 在本地DOM 树分支中执行。同样controllerpre-link 函数以自上而下 的方式遍历本地DOM 分支。之后post-link自下而上的方式执行。 如果你不理解它只是一团糟。它这样做是有原因的。 这是正确的技术答案,但是,我仍然有疑问何时应该使用链接功能。 我们应该到处使用controller 而不是link 吗?这样以后如果需要共享方法或要引入一些逻辑,我就不需要更改代码?一直使用controller而不是链接有什么陷阱吗?【参考方案6】:

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

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

对比

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

【讨论】:

当您对答案投反对票时,请发表评论以解释您的观点。谢谢 我不是反对者,但这并不完全正确,因为您仍然可以将任何所需的依赖项注入指令本身,例如:module.directive('myDirective', function($window) etc...。然后可以从链接函数内部访问它。 这似乎完全不正确,因为您可以将服务注入链接功能 @JoshRibakoff 最终结果是一样的,你可以访问链接功能中的服务。它是否在函数的参数中声明并不重要。在这方面,迈克·张伯伦是正确的 @cwyatt1 我在纠正用语,plnkr 没有显示注入到 link() 函数中,因为这不是 Angular 的功能。你可能认为我是迂腐的,但 metamatts 的评论已经概述了 plunkr 所做的与注入控制器所做的许多重要区别。 OP正在询问有什么区别,并且有区别。

以上是关于链接 vs 编译 vs 控制器的主要内容,如果未能解决你的问题,请参考以下文章

多个容器视图 VS 链接到单个容器视图的多个视图控制器

我用VS2010 编译的控制台应用 输出中文也是乱码。。请问您是怎么解决的?

vs code 编译python 输出到调试控制台

VS2019,调试的时候控制台无法显示输出,求教各位大佬这是怎么回事?

vs 命令行编译静态库

VS为VC++添加UAC控制(VC程序默认管理员运行)