前幾天在用textangular富文本編輯器插件時,將存儲的文本及格式存儲到數據庫中,但是從后台接口中再向angular頁面插入時卻不能執行,即在Angular中操作DOM沒有實現,后來查看了一下,操作DOM需要使用指令Directive封裝DOM,這樣嘗試之后終於解決了問題,從而對指令也有了很大的了解,以后再操作DOm就要小心了
以下是我查閱資料時看到的,感覺so詳細,解決了我很多issue,因此copy過來看看
原文http://www.cnblogs.com/xuema/p/4350747.html
創建指令
指令也是一種服務,只是這種服務的定義有幾個特殊要求:
- 必須使用模塊的directive()方法注冊服務
- 必須以對象工廠/factory()方法定義服務實現
- 對象工廠必須返回一個指令定義對象
- //定義指令的類工廠
- var directiveFactory = function(injectables){
- //指令定義對象
- var directiveDefinationObject = {
- ...
- };
- return directiveDefinationObject;
- };
- //在模塊上注冊指令
- angular.module("someModule",[])
- .directive("directiveName",directiveFactory);
INSIDE:指令在注入器中的登記名稱是:指令名+Directive。 例如,ng-app指令的服務名稱是:"ngAppDirective"。
示例(http://www.dwz.cn/26R4S5中“使用指令封裝DOM操作”第一頁)定義一個簡單的指令ez-hoverable,這個指令被限制只能 出現在屬性的位置,每個具有這個指令的HTML元素,將在鼠標移入 時以虛線邊框突出顯示。
指令定義對象
每個指令定義的工廠函數,需要返回一個指令定義對象。指令定義對象就是 一個具有約定屬性的JavaScript對象,編譯器/$compile在編譯時就根據這 個定義對象對指令進行展開。
指令定義對象的常用屬性如下:
- template : string
使用template指定的HTML標記替換指令內容(或指令自身)
- restrict : string
用來限定指令在HTML模板中出現的位置。
- replace : true|false
使用這個屬性指明template的替換方式。
- scope : true|false|{...}
scope屬性為指令創建私有的作用域,這在創建可復用的Widget時非常有用。
- link : function(..){...}
link屬性是一個函數,用來在指令中操作DOM樹、實現數據綁定。
- transclude : true|false|'element'
允許指令包含其他HTML元素,這通常用於實現一個容器類型的Widget。
template:定義替換模板
最簡單的指令只需要使用template屬性進行模板替換就可以實現。
template指明一個HTML片段,可以用來:
- 替換指令的內容。這是默認的行為,可以使用replace屬性更改。
- 如果replace = true,那么用HTML片段替換指令本身。
- 包裹指令的內容,如果transclue屬性為true。
示例(http://www.dwz.cn/26R4S5中“使用指令封裝DOM操作”第三頁)實現了一個ezCustomer指令,這個指令只是簡單的使用template指定的 模板替換ez-customer的內容:
restrict:限制指令的出現位置
restict屬性可以是EACM這四個字母的任意組合,用來限定指令的應用場景。 如果不指定這個屬性,默認情況下,指令將僅允許被用作元素名和屬性名:
- E - 指令可以作為HTML元素使用
- A - 指令可以作為HTML屬性使用
- C - 指令可以作為CSS類使用
- M - 指令可以在HTML注釋中使用
我們對之前的示例,增加一個restrict屬性,限制這個只能作為元素名使用。 代碼已經預置到右邊,你可以看到,現在唯一合法的方式是使用如下方式應用指令:
- <ez-customer></ez-customer>
考查編譯后的DOM結構,你會發現ez-customer這個”偽“HTML標簽還被保留着,這有時讓完美 主義者有點鬧心:
replace:模板的使用方式
我們希望使用template完整地替換原始的DOM對象,而不是填充其內容,replace 屬性負責這件事。
replace屬性指明使用template時,如何替換指令元素:
- true - 編譯時,將使用template替換指令元素
- false - 編譯時,將使用template替換指令元素的內容
示例(http://www.dwz.cn/26R4S5中“使用指令封裝DOM操作”第五頁)增加了replace屬性,值為true意味着這個指令要求編譯器使用template 替換原始的DOM元素:
你可能注意到模板的內容稍微修改了一下,這是因為replace為true時,要求模板有 一個根節點。
作用域問題
默認情況下,指令沒有自己的scope對象,換句話說,它使用所在DOM對象對應的scope對象。
那么問題來了,如果一個指令在同一個scope內出現多次,會怎樣?
- <div ng-controller="ezCtrl">
- <ez-customer></ez-customer>
- <ez-customer></ez-customer>
- </div>
沒錯,由於兩個ez-customer指令都處在ezCtrl開辟的作用域內,所以兩個指令綁定到了同樣的 數據模型上,得到的是重復的結果。
顯然,我們可以將每個ez-customer指令置於不同的作用域下,這意味着我們給每個ez-customer 一個不同的控制器:
- <div ng-controller="ezCtrl1">
- <ez-customer></ez-customer>
- </div>
- <div ng-controller="ezCtrl2">
- <ez-customer></ez-customer>
- </div>
看起來很怪異,對嗎?
scope:使用隔離的作用域
通過設置scope屬性,指令的每個實例都將獲得一個隔離的本地作用域:
- var ezCustomerDirectiveFactory = function(){
- return {
- restrict:"E",
- replace:true,
- scope:{
- name : "@name",
- address : "=address"
- },
- template:"<div>name:{{name}} address:{{address}}</div>"
- }
- }
在上面的例子中,我們在本地scope上定義了兩個屬性:name和address,這樣在 模板中就可以使用name和address了。
你應該已經注意到,name屬性的值之前有一個@符號,這是一個約定好的標記,它 告訴編譯器,本地scope上的name值需要從應用這個指令的DOM元素的name屬性值 讀取,如果DOM元素的name屬性值變了,那么本地scope上的name值也會變化。
同樣,address屬性之前的=符號也是一個約定好的標記,它告訴編譯器,本地scope 上的address屬性值和DOM元素的address屬性值指定的外部scope對象上的模型需要 建立雙向連接:外部scope上模型的變化會改變本地scope上的address屬性,本地 scope上address屬性的變化也會改變外部scope上模型的變化。
有點繞,上個圖:
從圖中可以看出:
- 指令的template綁定的是本地scope上的name和address。
- 本地scope的name屬性的值始終是ez-customer對象上name屬性的值
- 本地scope的address屬性值始終和ez-customer對應的scope對象上的Emmy.address 保持同步。
link:在指令中操作DOM
如果需要在指令中操作DOM,我們需要在對象中定義link屬性,link函數的定義如下:
- function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
注意link函數的參數,AngularJS在編譯時負責傳入正確的值:
- scope
指令對應的scope對象。如果指令沒有定義自己的本地作用域,那么傳入的就是外部的 作用域對象。
- iElement
指令所在DOM對象的jqLite封裝。如果使用了template屬性,那么iElement對應 變換后的DOM對象的jqLite封裝。
- iAttrs
指令所在DOM對象的屬性集。這是一個Hash對象,每個鍵是駝峰規范化后 的屬性名。
后兩個參數我們先略過。
示例
示例(http://www.dwz.cn/26R4S5中“使用指令封裝DOM操作”第八頁)中,我們實現了一個可以指定顯示格式的小時鍾指令:ezCurrentTime。和原來一樣, 我們在link函數中啟動定時器,並在定時器中更新DOM。有幾點解釋下:
- 我們在scope上使用$watch()方法對format的值進行監聽,並使用這個值調整顯示格式
- 我們監聽element的$destroy事件,這個事件是在DOM對象銷毀時觸發。我們在這個事件觸發時 銷毀定時器以釋放資源
- 我們使用了AngularJS內置的$interval服務,而不是setInterval()函數創建定時器。
- 我們使用了AngularJS內置的dateFilter過濾器服務,對時間的顯示進行格式化。 和$interval一樣,dateFilter服務也是通過注入器注入的。
transclude:包含其他元素
有些指令需要能夠包含其他未知的元素。比如我們定義一個指令ez-dialog,用來 封裝對話框的樣式和行為,它應當允許在使用期(也就是在界面模板文件里)才指 定其內容:
- <ez-dialog>
- <p>對話框的內容在我們開發ez-dialog指令的時候是無法預計的。這部分內容需要
- 被轉移到展開的DOM樹中適當的位置。</p>
- </ez-dialog>
transclude屬性可以告訴編譯器,利用所在DOM元素的內容,替換template中包含 ng-transclude指令的元素的內容:
從上圖中可以看到,使用transclude有兩個要點:
- 需要首先聲明transclude屬性值為true,這將告訴編譯器,使用我們這個指令的 DOM元素,其內容需要被復制並插入到編譯后的DOM樹的某個點。
- 需要在template屬性值中使用ng-transclude指明插入點。
右邊嵌入了ez-dialog的實現實例(http://www.dwz.cn/26R4S5中“使用指令封裝DOM操作”第九頁)。