### 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