有人可以用简单的语言解释一下吗?
文档看起来有点迟钝。我没有得到何时使用一个而不是另一个的本质和全局。两个对比的例子很棒。
答案 0 :(得分:217)
编译函数 - 用于模板 DOM操作(即操作tElement = template元素),因此适用于与指令关联的模板的所有DOM克隆的操作。
链接函数 - 用于注册DOM侦听器(即实例范围上的$ watch表达式)以及实例 DOM操作(即,操作iElement =单个实例元素)。
它在克隆模板后执行。例如,在< ling-repeat ...>内,在< li>之后执行链接功能。模板(tElement)已被克隆(进入iElement),用于该特定的< li> 。$ A watch()允许指令通知实例范围属性更改(实例范围与每个实例关联),这允许指令将更新的实例值呈现给DOM - 通过复制从实例范围到DOM的内容。
请注意,DOM转换可以在编译功能和/或链接功能中完成。
大多数指令只需要一个链接函数,因为大多数指令只处理特定的DOM元素实例(及其实例范围)。
帮助确定使用哪种方法的一种方法:考虑编译函数没有收到scope
参数。 (我故意忽略了transclude链接函数参数,它接收了一个被转换的范围 - 这是rarely使用的。)所以编译函数不能做任何你想做的事情,需要一个(实例)范围 - 你不能$ watch任何模型/实例范围属性,你不能使用实例范围信息操纵DOM,你不能调用实例范围上定义的函数等。
但是,编译功能(如链接功能)可以访问属性。因此,如果您的DOM操作不需要实例范围,则可以使用编译功能。由于这些原因,这里是仅使用编译函数的指令的an example。它检查属性,但它不需要实例范围来完成它的工作。
这是指令的an example,它也只使用编译功能。该指令只需要转换模板DOM,因此可以使用编译函数。
另一种帮助确定使用哪种方法的方法:如果你不在链接函数中使用“element”参数,那么你可能不需要链接函数。
由于大多数指令都有链接功能,我不打算提供任何示例 - 它们应该很容易找到。
请注意,如果您需要编译函数和链接函数(或前后链接函数),则编译函数必须返回链接函数,因为如果'compile'属性是,则忽略'link'属性定义
另见
答案 1 :(得分:70)
我把头撞到了墙上几天,我觉得还有更多的解释。
基本上,文档提到分离主要是性能增强。我要重申,编译阶段主要用于需要在编译子元素之前修改DOM。
出于我们的目的,我会强调术语,否则会引起混淆:
编译器SERVICE($ compile)是一种角度机制,它处理DOM并在指令中运行各种代码。
编译FUNCTION是指令中的一位代码,它在特定时间由编译器SERVICE($ compile)运行。
关于编译功能的一些注意事项:
您无法修改ROOT元素(您的指令影响的元素),因为它已经从DOM的外层编译(编译SERVICE已经扫描了该元素的指令)。
如果要将其他指令添加到(嵌套)元素,您可以:
必须在编译阶段添加它们。
必须将编译服务注入链接阶段并手动编译元素。但是,要小心两次编译!
查看$ compile的嵌套和显式调用是如何工作的也很有帮助,所以我创建了一个可以在http://jsbin.com/imUPAMoV/1/edit查看的操场。基本上,它只是将步骤记录到console.log。
我将在此处说明您在该垃圾箱中看到的结果。对于自定义指令的DOM,tp和sp嵌套如下:
<tp>
<sp>
</sp>
</tp>
Angular compile SERVICE将调用:
tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link
jsbin代码还有tp post-link FUNCTION在第三个指令(up)上显式调用compile SERVICE,最后执行所有三个步骤。
现在,我想通过几个场景来展示如何使用编译和链接来做各种事情:
情景1:作为MACRO的指令
您希望动态地将指令(例如ng-show)添加到模板中可以从属性派生的内容中。
假设您有一个指向:
的templateUrl<div><span><input type="text"></span><div>
你需要一个自定义指令:
<my-field model="state" name="address"></my-field>
将DOM转换为:
<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>
基本上,您希望通过使用指令可以解释的一致模型结构来减少样板。换句话说:你想要一个宏。
这对于编译阶段非常有用,因为您可以根据属性将所有DOM操作基于您所知道的事情。只需使用jQuery添加属性:
compile: function(tele, tattr) {
var span = jQuery(tele).find('span').first();
span.attr('ng-show', tattr.model + ".visible." + tattr.name);
...
return {
pre: function() { },
post: function() {}
};
}
操作顺序是(你可以通过前面提到的jsbin看到这个):
在上面的例子中,不需要链接,因为所有指令的工作都是在编译功能中完成的。
在任何时候,指令中的代码都可以要求编译器SERVICE在其他元素上运行。
这意味着如果您注入编译服务,我们可以在链接函数中执行完全相同的操作:
directive('d', function($compile) {
return {
// REMEMBER, link is called AFTER nested elements have been compiled and linked!
link: function(scope, iele, iattr) {
var span = jQuery(iele).find('span').first();
span.attr('ng-show', iattr.model + ".visible." + iattr.name);
// CAREFUL! If span had directives on it before
// you will cause them to be processed again:
$compile(span)(scope);
}
});
如果您确定要传递给$ compile SERVICE的元素最初是无指令的(例如,它们来自您定义的模板,或者您刚刚使用angular.element()创建它们),那么最终结果与以前几乎相同(尽管你可能会重复一些工作)。但是,如果元素上有其他指令,则只会导致再次处理这些指令,这会导致各种不稳定的行为(例如事件和手表的双重注册)。
因此,编译阶段是宏观工作的更好选择。
SCENARIO 2:通过范围数据进行DOM配置
这一点来自上面的例子。假设您在操作DOM时需要访问作用域。那么,在这种情况下,编译部分对你来说是无用的,因为它发生在范围可用之前。
所以,让我们假设您想要使用验证来输出输入,但是您希望从服务器端ORM类(DRY)导出验证,并让它们自动应用并生成正确的客户端这些验证的用户界面。
你的模特可能会推动:
scope.metadata = {
validations: {
address: [ {
pattern: '^[0-9]',
message: "Address must begin with a number"
},
{ maxlength: 100,
message: "Address too long"
} ]
}
};
scope.state = {
address: '123 Fern Dr'
};
你可能想要一个指令:
<form name="theForm">
<my-field model="state" metadata="metadata" name="address">
</form>
自动包含正确的指令和div以显示各种验证错误:
<form name="theForm">
<div>
<input ng-model="state.address" type="text">
<div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...
在这种情况下,您肯定需要访问范围(因为这是存储验证的地方),并且必须手动编译添加内容,再次注意不要双重编译。 (作为旁注,您需要在包含的表单标记上设置一个名称(我在这里假设该表格),并且可以通过链接访问iElement.parent()。controller(&#39; form&#) 39;)$名)
在这种情况下,编写编译函数没有意义。链接真的是你想要的。步骤将是:
像这样:
angular.module('app', []).
directive('my-field', function($compile) {
return {
link: function(scope, iele, iattr) {
// jquery additions via attr()
// remove ng attr from top-level iele (to avoid duplicate processing)
$compile(iele)(scope); // will pick up additions
}
};
});
当然,您可以逐个编译嵌套元素,以避免在再次编译顶级元素时担心ng指令的重复处理。
关于这种情况的最后一点说明:我暗示你要从服务器推送验证的定义,在我的例子中,我已将它们显示为范围内的数据。我把它作为练习让读者弄清楚如何处理需要从REST API中提取数据的方法(提示:延迟编译)。
情景3:通过链接进行双向数据绑定
当然,链接最常见的用途是通过watch / apply简单地连接双向数据绑定。大多数指令属于这一类,因此在其他地方已经充分涵盖。
答案 2 :(得分:50)
编译器
编译器是一种角度服务,它遍历DOM以查找属性。编译过程分为两个阶段。
编译:遍历DOM并收集所有指令。结果是一个链接功能。
- 醇>
链接:将指令与范围相结合并生成实时视图。范围模型中的任何更改都会反映在视图中,并且与视图的任何用户交互都会反映在范围模型中。使范围模型成为事实的唯一来源。
某些指令(如
ng-repeat
)为集合中的每个项目克隆DOM元素一次。编译和链接阶段可以提高性能,因为克隆模板只需要编译一次,然后为每个克隆实例链接一次。
因此,至少在某些情况下,这两个阶段作为优化单独存在。
如果要进行DOM转换,则应为
compile
。如果您想添加一些行为更改的功能,则应该在link
。
答案 3 :(得分:18)
这是来自Misko关于指令的谈话。 http://youtu.be/WqmeI5fZcho?t=16m23s
将编译器功能视为 在模板上工作的东西和允许的东西 例如,通过向其添加类来更改模板本身 这样的事情。但它实际上是连接功能 因为链接功能具有将两者绑定在一起的工作 访问范围,它是执行一次的链接功能 对于特定模板的每个实例化。所以唯一的一种 你可以在编译函数中放置的东西就是那些东西 在所有情况下都很常见。
答案 4 :(得分:10)
线程迟到了。但是,为了未来读者的利益:
我看到了以下视频,它以非常好的方式解释了Angular JS中的编译和链接:
https://www.youtube.com/watch?v=bjFqSyddCeA
在此处复制/输入所有内容并不令人满意。我从视频中截取了几个屏幕截图,它解释了编译和链接阶段的每个阶段:
第二个截图有点令人困惑。但是,如果我们按照步骤编号进行操作,那就很简单了。
第一个周期:&#34;编译&#34;首先对所有指令执行 第二周期:&#34;控制器&#34;和&#34; Pre-Link&#34;得到执行(只是一个接一个) 第三个周期:&#34;后链接&#34;以相反的顺序执行(从最里面开始)
以下是代码,演示了上述内容:
var app = angular.module('app', []); app.controller('msg', ['$scope', function($scope){ }]); app.directive('message', function($interpolate){ return{ compile: function(tElement, tAttributes){ console.log(tAttributes.text + " -In compile.."); return { pre: function(scope, iElement, iAttributes, controller){ console.log(iAttributes.text + " -In pre.."); }, post: function(scope, iElement, iAttributes, controller){ console.log(iAttributes.text + " -In Post.."); } } }, controller: function($scope, $element, $attrs){ console.log($attrs.text + " -In controller.."); }, } });
<body ng-app="app">
<div ng-controller="msg">
<div message text="first">
<div message text="..second">
<div message text="....third">
</div>
</div>
</div>
</div>
<强>更新强>
同一视频的第2部分可在此处获取:https://www.youtube.com/watch?v=1M3LZ1cu7rw该视频在一个简单示例中解释了有关如何在Angular JS的编译和链接过程中修改DOM和处理事件的更多信息。
答案 5 :(得分:6)
两个阶段:编译和链接
编译:
遍历DOM树,查找指令(元素/属性/类/注释)。指令的每个编译都可以修改其模板,或修改尚未编译的内容。一旦指令匹配,它就会返回一个链接函数,该函数在稍后阶段用于将元素链接在一起。在编译阶段结束时,我们有一个已编译的指令列表及其相应的链接函数。
链接:
当链接元素时,DOM树在DOM树的分支点处被破坏,并且内容被模板的已编译(和链接)实例替换。原始置换内容要么被丢弃,要么在转换的情况下重新链接回模板。通过翻转,两个部分连接在一起(有点像链,模板片位于中间)。调用链接函数时,模板已绑定到范围,并添加为元素的子元素。链接功能是您进一步操作DOM并设置更改侦听器的机会。
答案 6 :(得分:3)
这个问题已经过时了,我想做一些可能会有所帮助的简短摘要: