有人能简单解释一下吗?
文档似乎有点迟钝。我没有得到什么时候使用一个而不是另一个的本质和大局。对比两者的例子会很棒。
有人能简单解释一下吗?
文档似乎有点迟钝。我没有得到什么时候使用一个而不是另一个的本质和大局。对比两者的例子会很棒。
compile 函数 - 用于模板DOM 操作(即,tElement = 模板元素的操作),因此操作适用于与指令关联的模板的所有 DOM 克隆。
链接函数 - 用于注册 DOM 侦听器(即实例范围内的 $watch 表达式)以及实例DOM 操作(即,对 iElement = 单个实例元素的操作)。
它在模板被克隆后执行。例如,在 <li ng-repeat...> 中,链接函数在 <li> 模板 (tElement) 为该特定 <li> 元素克隆(到 iElement)之后执行。
$watch() 允许指令收到实例范围属性更改的通知(实例范围与每个实例相关联),这允许指令将更新的实例值呈现给 DOM——通过将内容从实例范围复制到DOM。
请注意,DOM 转换可以在 compile 函数和/或 link 函数中完成。
大多数指令只需要一个链接函数,因为大多数指令只处理特定的 DOM 元素实例(及其实例范围)。
一种帮助确定使用哪个的方法:考虑 compile 函数没有接收 scope
参数。(我故意忽略了 transclude 链接函数参数,它接收一个 transcluded 作用域——这很少使用。)所以 compile 函数不能做任何你想做的需要(实例)作用域的事情——你可以't $watch 任何模型/实例范围属性,您不能使用实例范围信息操作 DOM,您不能调用实例范围上定义的函数等。
但是,编译函数(如链接函数)确实可以访问这些属性。因此,如果您的 DOM 操作不需要实例范围,您可以使用 compile 函数。由于这些原因,这是一个仅使用编译函数的指令示例。它检查属性,但不需要实例范围来完成其工作。
这是一个仅使用编译函数的指令示例。该指令只需要转换模板 DOM,因此可以使用 compile 函数。
帮助确定使用哪个的另一种方法:如果您不在链接函数中使用“元素”参数,那么您可能不需要链接函数。
由于大多数指令都具有链接功能,因此我不打算提供任何示例——它们应该很容易找到。
请注意,如果您需要一个编译函数和一个链接函数(或前链接和后链接函数),则编译函数必须返回链接函数,因为如果定义了 'compile' 属性,则会忽略 'link' 属性。
也可以看看
我在这上面撞墙了几天,我觉得需要多解释一些。
基本上,文档提到分离在很大程度上是一种性能增强。我要重申的是,编译阶段主要用于在编译子元素本身之前需要修改 DOM 时。
为了我们的目的,我将强调术语,否则会令人困惑:
编译器 SERVICE ($compile) 是处理 DOM 并运行指令中的各种代码位的角度机制。
compile FUNCTION 是指令中的一小段代码,它在特定时间由编译器 SERVICE ($compile) 运行。
关于 compile FUNCTION 的一些注意事项:
您不能修改 ROOT 元素(您的指令影响的那个),因为它已经从 DOM 的外部级别编译(编译服务已经扫描了该元素上的指令)。
如果您想向(嵌套)元素添加其他指令,您可以:
必须在编译阶段添加它们。
必须将编译服务注入链接阶段并手动编译元素。但是,小心编译两次!
了解对 $compile 的嵌套和显式调用如何工作也很有帮助,因此我创建了一个用于在http://jsbin.com/imUPAMoV/1/edit 上查看的游乐场。基本上,它只是将步骤记录到 console.log。
我会在这里说明你在那个垃圾箱里看到的结果。对于自定义指令 tp 和 sp 的 DOM 嵌套如下:
<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:指令作为宏观
您想动态地将指令(例如 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 看到这一点):
在上面的例子中,不需要链接,因为指令的所有工作都是在 compile FUNCTION 中完成的。
在任何时候,指令中的代码都可以要求编译器 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() 创建它们),那么最终结果几乎是和以前一样(虽然你可能会重复一些工作)。但是,如果元素上有其他指令,您只会导致再次处理这些指令,这可能会导致各种不稳定的行为(例如,事件和监视的双重注册)。
因此,编译阶段是宏风格工作更好的选择。
场景 2:通过范围数据配置 DOM
这是从上面的例子中得出的。假设您在操作 DOM 时需要访问范围。那么,在这种情况下,编译部分对您来说是无用的,因为它发生在作用域可用之前。
因此,假设您想通过验证导出输入,但您想从服务器端 ORM 类 (DRY) 导出验证,并让它们自动应用并为这些验证生成正确的客户端 UI。
您的模型可能会推送:
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>
...
在这种情况下,您肯定需要访问范围(因为这是存储验证的地方),并且必须手动编译添加内容,再次注意不要重复编译。(作为旁注,您需要在包含表单标签上设置一个名称(我在这里假设为 theForm),并且可以通过 iElement.parent().controller('form').$name 链接访问它.
在这种情况下,编写编译函数没有意义。链接真的是你想要的。步骤是:
像这样:
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:通过链接进行双向数据绑定
当然,link 最常见的用途是通过 watch/apply 简单地连接双向数据绑定。大多数指令都属于这一类,因此在其他地方有充分的介绍。
编译器
Compiler 是一个 Angular 服务,它遍历 DOM 寻找属性。编译过程分为两个阶段。
编译:遍历 DOM 并收集所有指令。结果是一个链接函数。
链接:将指令与范围结合并生成实时视图。范围模型中的任何更改都会反映在视图中,并且任何用户与视图的交互都会反映在范围模型中。使范围模型成为单一的事实来源。
一些指令,例如
ng-repeat
为集合中的每个项目克隆一次 DOM 元素。具有编译和链接阶段可提高性能,因为克隆模板只需编译一次,然后为每个克隆实例链接一次。
因此,至少在某些情况下,这两个阶段作为优化单独存在。
如果您要进行 DOM 转换,则应该是
compile
. 如果你想添加一些行为改变的特性,它应该在link
.
这是来自 Misko 关于指令的演讲。http://youtu.be/WqmeI5fZcho?t=16m23s
将编译器函数视为在模板上工作的东西,以及允许通过例如向模板添加类或类似方式来更改模板本身的东西。但实际上是链接函数完成了将两者绑定在一起的工作,因为链接函数可以访问作用域,并且链接函数为特定模板的每个实例化执行一次。因此,您可以放置在 compile 函数中的唯一东西是所有实例中通用的东西。
有点晚了。但是,为了未来读者的利益:
我看到了以下视频,它以一种非常棒的方式解释了 Angular JS 中的编译和链接:
https://www.youtube.com/watch?v=bjFqSyddCeA
在这里复制/输入所有内容是不愉快的。我从视频中截取了几个屏幕截图,其中解释了编译和链接阶段的每个阶段:
第二个屏幕截图有点令人困惑。但是,如果我们按照步骤编号,则非常简单。
第一个循环:首先对所有指令执行“编译”。
第二个周期:“控制器”和“预链接”被执行(一个接一个) 第三个周期:“后链接”以相反的顺序执行(从最里面开始)
以下是代码,它演示了上述内容:
var app = angular.module('app', []); app.controller('msg', ['$scope', function($scope){ }]); app.directive('message', function($interpolate){ 返回{ 编译:函数(tElement,tAttributes){ console.log(tAttributes.text + " -In compile.."); 返回 { 前:功能(范围,iElement,iAttributes,控制器){ console.log(iAttributes.text + " -In pre.."); }, 帖子:功能(范围,iElement,iAttributes,控制器){ console.log(iAttributes.text + " -In Post.."); } } }, 控制器:函数($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 和处理事件.