### 18.5. Compile的细节
指令的处理过程,是 ng 的 _Compile_ 过程的一部分,它们也是紧密联系的。继续深入指令的定义方法,首先就要对 Compile 的过程做更细致的了解。
前面说过, ng 对页面的处理过程:
- 浏览器把 HTML 字符串解析成 DOM 结构。
- ng 把 DOM 结构给 `$compile` ,返回一个 `link` 函数。
- 传入具体的 `scope` 调用这个 `link` 函数。
- 得到处理后的 DOM ,这个 DOM 处理了指令,连接了数据。
`$compile` 最基本的使用方式:
var link = $compile('<p>{{ text }}</p>');
var node = link($scope);
console.log(node);
上面的 `$compile` 和 `link` 调用时都有额外参数来实现其它功能。先看 `link` 函数,它形如:
function(scope[, cloneAttachFn]
第二个参数 `cloneAttachFn` 的作用是,表明是否复制原始节点,及对复制节点需要做的处理,下面这个例子说明了它的作用:
<div ng-controller="TestCtrl"></div>
<div id="a">A {{ text }}</div>
<div id="b">B </div>
app.controller('TestCtrl', function($scope, $compile){
var link = $compile($('#a'));
//true参数表示新建一个完全隔离的scope,而不是继承的child scope
var scope = $scope.$new(true);
scope.text = '12345';
//var node = link(scope, function(){});
var node = link(scope);
$('#b').append(node);
});
`cloneAttachFn` 对节点的处理是有限制的,你可以添加 `class` ,但是不能做与数据绑定有关的其它修改(修改了也无效):
app.controller('TestCtrl', function($scope, $compile){
var link = $compile($('#a'));
var scope = $scope.$new(true);
scope.text = '12345';
var node = link(scope, function(clone_element, scope){
clone_element.text(clone_element.text() + ' ...'); //无效
clone_element.text('{{ text2 }}'); //无效
clone_element.addClass('new_class');
});
$('#b').append(node);
});
修改无效的原因是,像 `{{ text }}` 这种所谓的 _Interpolate_ 在 `$compile` 中已经被处理过了,生成了相关函数(这里起作用的是 `directive` 中的一个 `postLink` 函数),后面执行 `link` 就是执行了 `$compile` 生成的这些函数。当然,如果你的文本没有数据变量的引用,那修改是会有效果的。
前面在说自定义指令时说过, `link` 函数是由 `compile` 函数返回的,也就像前面说的,应该把改变 DOM 结构的逻辑放在 `compile` 函数中做。
`$compile` 还有两个额外的参数:
$compile(element, transclude, maxPriority);
`maxPriority` 是指令的权重限制,这个容易理解,后面再说。
`transclude` 是一个函数,这个函数会传递给 `compile` 期间找到的 `directive` 的 `compile` 函数(编译节点的过程中找到了指令,指令的 `compile` 函数会接受编译时传递的 `transclude` 函数作为其参数)。
但是在实际使用中,除我们手工在调用 `$compile` 之外,初始化时的根节点 `compile` 是不会传递这个参数的。
在我们定义指令时,它的 `compile` 函数是这个样子的:
function compile(tElement, tAttrs, transclude) { ... }
事实上, `transclude` 的值,就是 `directive` 所在的 **原始** 节点,把原始节点重新做了编译之后得到的 `link` 函数(需要 `directive` 定义时使用 `transclude` 选项),后面会专门演示这个过程。所以,官方文档上也把 `transclude` 函数描述成 `link` 函数的样子(如果自定义的指令只用在自己手动 `$compile` 的环境中,那这个函数的形式是可以随意的):
{function(angular.Scope[, cloneAttachFn]}
所以记住,定义指令时, `compile` 函数的第三个参数 `transclude` ,就是一个 `link` ,装入 `scope` 执行它你就得到了一个节点。
- Introduction
- 关于AngularJS
- 关于本文档
- 开始的例子
- 依赖注入
- 作用域
- 数据绑定与模板
- 数据->模板
- 模板->数据
- 数据->模板->数据->模板
- 模板
- 定义模板内容
- 内容渲染控制
- 节点控制
- 事件绑定
- 表单控件
- 模板中的过滤器
- 排序 orderBy
- 过滤列表 filter
- 其它
- 例子:表头排序
- 例子:搜索
- 锚点路由
- 路由定义
- 参数定义
- 业务处理
- 定义模板变量标识标签
- AJAX
- HTTP请求
- 广义回调管理
- 工具函数
- 上下文绑定
- 对象处理
- 类型判定
- 其它服务
- 日志
- 缓存
- 计时器
- 表达式函数化
- 模板单独使用
- 自定义模块和服务
- 模块和服务的概念与关系
- 定义模块
- 定义服务
- 引入模块并使用服务
- 附加模块 ngResource
- 使用引入与整体概念
- 基本定义
- 基本使用
- 定义和使用时的占位量
- 实例
- AngularJS与其它框架的混用(jQuery, Dojo)
- 自定义过滤器
- 自定义指令directive
- 指令的使用
- 指令的执行过程
- 基本的自定义方法
- 属性值类型的自定义
- Compile的细节
- transclude的细节
- 把节点内容作为变量处理的类型
- 指令定义时的参数
- Attributes的细节
- 预定义的 NgModelController
- 预定义的 FormController
- 示例:文本框
- 示例:模板控制语句 for
- 示例:模板控制语句 if/else