AngularJs應用如今愈來愈流行了,谷歌都與微軟合做支持AngularJS2.0,這是要逆天了,說明AngularJs未來大勢所趨。最近想跳槽,又從新拾起了AngluarJs(以前因爲缺乏項目應用,一直都是學了就忘記了),也寫寫複習的知識點,但願此次能讓我對AngularJs的理解更深刻透徹。css
AngularJS指令使咱們用來擴展瀏覽器能力的技術之一。在DOM編譯期間,和HTML關聯着的指令會被檢測到,而且被執行。這使得指令能夠爲DOM指定行爲,或者改變它。本質上就是AngularJS擴展具備自定義功能的HTML元素的途徑。html
AngularJS有一套完整的、可擴展的、用來幫助web應用開發的指令集,它使得HTML能夠轉變成「特定領域語言(DSL)」。web
在開始自定義指令以前,咱們有必要了解一下指令在框架中的執行流程。以下:express
1.瀏覽器獲得 HTML 字符串內容,解析獲得 DOM 結構。編程
你須要認清這一點,由於咱們的模板必須是可被解析的HTML。這是AngularJS和那些「以字符串爲基礎而非以DOM元素爲基礎的」模板系統的區別之處。數組
2.ng 引入,把 DOM 結構扔給 $compile 函數處理:
① 找出 DOM 結構中有變量佔位符瀏覽器
② 匹配找出 DOM 中包含的全部指令引用app
③ 把指令關聯到 DOM框架
④ 關聯到 DOM 的多個指令優先級排列異步
⑤ 執行指令中的 compile 函數(改變 DOM 結構,返回 link 函數)
⑥ 獲得的全部 link 函數組成一個列表做爲 $compile 函數的返回
3. 執行 link 函數(鏈接模板的 scope)。
經過調用連接函數來將模板與做用域連接起來。這會輪流調用每個指令的連接函數,讓每個指令都能對DOM註冊監聽事件,和創建對做用域的的監聽。這樣最後就造成了做用域的DOM的動態綁定。任何一個做用域的改變都會在DOM上體現出來。
這裏注意區別一下$compile和compile,前者是ng內部的編譯服務,後者是指令中的編譯函數,二者發揮做用的範圍不一樣。compile和link函數息息相關又有所區別。瞭解執行流程對後面的理解會有幫助。
理解一下編譯函數與連接函數:
編譯函數 - 編譯函數在指令中是不多的, 由於大部分指令都只是爲了處理相應的DOM元素實例,而不是改變模板DOM元素。考慮到性能問題,任何指令的實例見能被共享的操做都應該移到編譯函數中。
連接函數 - 指令不多不帶有連接函數,連接函數可讓指令對相應克隆元素註冊事件,還能夠將做用域中的內容複製到DOM中。
指令的幾種使用方式以下:
做爲標籤:<my-dir></my-dir>
做爲屬性:<span my-dir="exp"></span>
做爲註釋:<!-- directive: my-dir exp -->
做爲類名:<span class="my-dir: exp;"></span>
關於選擇以哪種方式指令,建議堅持使用屬性方式,由於它有較好的跨瀏覽器兼容。
關於自定義指令的命名,咱們是能夠隨意命名的。注意,全部內置指令的命名空間都使用ng做爲前綴。爲了防止命名空間衝突,不要在自定義指令前加ng前綴。另一個需知道的地方,指令命名時用駝峯規則,使用時用-分割各單詞。如:定義myDirective,使用時像這樣:<my-directive>。也建議你們寫指令時,統一以my-打頭作好規範,易於區分。
myModule.directive('namespaceDirectiveName', function factory(injectables) { var directiveDefinitionObject = { restrict: string,//指令的使用方式,包括標籤,屬性,類,註釋 priority: number,//指令執行的優先級 若是有ng-init定義了,則它優先級最高 template: string,//指令使用的模板,用HTML字符串的形式表示 templateUrl: string,//從指定的url地址加載模板 replace: bool,//是否用模板替換當前元素,若爲false,則append在當前元素上 transclude: bool,//是否將當前元素的內容轉移到模板中 scope: bool or object,//指定指令的做用域 controller: function controllerConstructor($scope, $element, $attrs, $transclude){...},//定義與其餘指令進行交互的接口函數 require: string,//指定須要依賴的其餘指令 link: function postLink(scope, iElement, iAttrs) {...},//以編程的方式操做DOM,包括添加監聽器等 compile: function compile(tElement, tAttrs, transclude){ return: { pre: function preLink(scope, iElement, iAttrs, controller){...}, post: function postLink(scope, iElement, iAttrs, controller){...} } }//編程的方式修改DOM模板的副本,能夠返回連接函數 }; return directiveDefinitionObject; });
Directive Definition Object 指令定義對象標準解釋:
指令定義對象給編譯器提供了生成指令須要的細節。這個對象的屬性有:
名稱name - 當前做用域的名稱,在註冊是可選的。
優先級priority - 當一個DOM上有多個指令時,有會須要指定指令執行的順序。 這個優先級就是用來在執行指令的compile函數前先排序的。高優先級的先執行。 相同優先級的指令順序沒有被指定誰先執行。
終端terminal - 若是被設置爲true,那麼該指令就會在同一個DOM的指令集和中最後被執行。任何其餘「terminal」的指令也仍然會執行,由於同級的指令順序是沒有被定義的。
做用域scope- 若是被定義成:
那麼就會爲當前指令建立一個新的做用域。若是有多個在同一個DOM上的指令要求建立新做用域,那麼只有一個新的會被建立。 這一建立新做用域的規則不適用於模板的根節點,由於模板的根節點老是會獲得一個新的做用域。
{} 對象哈希 - 那麼一個新的「孤立的」做用域就會被建立。這個「孤立的」做用域區別於通常做用域的地方在於,它不會以原型繼承的方式直接繼承自父做用域。這對於建立可重用的組件是很是有用的,由於可重用的組件通常不該該讀或寫父做用域的數據。 這個「孤立的」做用域使用一個對象哈希來表示,這個哈希定義了一系列本地做用域屬性, 這些本地做用域屬性是從父做用域中衍生出來的。這些屬性主要用來分析模板的值。這個哈希的鍵值對是本地屬性爲鍵,它的來源爲值。
@ 或 @attr - 將本地做用域成員成員和DOM屬性綁定。綁定結果老是一個字符串,由於DOM的屬性就是字符串。若是DOM屬性的名字沒有被指定,那麼就和本地屬性名同樣。好比說<widget my-attr="hello {{name}}"> 和做用域對象: { localName:'@myAttr' }。當name值改變的時候, 做用域中的LocalName也會改變。這個name是從父做用域中讀來的(而不是組件做用域)。
= 或 =expression(表達式) - 在本地做用域屬性和父做用域屬性間創建一個雙向的綁定。若是沒有指定父做用域屬性名稱,那就和本地名稱同樣。 好比 <widget my-attr="parentModel"> 和做用域對象: { localModel:'=myAttr' }, 本地屬性localModel會反映父做用域中parentModel的值。localModel和parentModel的任一方改變都會影響對方。
& 或 &attr - 提供了一種能在父做用域下執行表達式的方法。若是沒有指定父做用域屬性名稱,那就和本地名稱同樣。 好比 <widget my-attr="count = count + value">和做用域對象:{ localFn:'increment()' }。本地做用域成員localFn會指向一個increment表達式的函數包裝。一般你能夠經過這個表達式從本地做用域給父做用域傳值, 操做方法是將本地變量名和值得對應關係傳給這個表達式的包裝函數。好比說,這個表達式是increment(amount),那麼你就能夠用調用localFn({amount:22})的方式指定amount的值。
控制器controller - 控制器的構造對象。這個控制器函數是在預編譯階段被執行的,而且它是共享的,其餘指令能夠經過它的名字獲得(參考依賴屬性[require attribute])。這就使得指令間能夠互相交流來擴大本身的能力。會傳遞給這個函數的參數有:
$scope - 當前元素關聯的做用域。
$element - 當前元素
$attrs - 當前元素的屬性對象。
$transclude - 模板連接功能前綁定到正確的模板做用域:function(cloneLinkingFn)。
請求require - 請求將另外一個控制器做爲參數傳入到當前連接函數。 這個請求須要傳遞被請求指令的控制器的名字。若是沒有找到,就會觸發一個錯誤。請求的名字能夠加上下面兩個前綴:
? - 不要觸發錯誤,這只是一個可選的請求。
^ - 沒找到的話,在父元素的controller裏面也查找有沒有。
限制restrict - EACM中的任意一個之母。它是用來限制指令的聲明格式的。若是沒有這一項。那就只容許使用屬性形式的指令。
E - 元素名稱:<my-directive></my-directive>
A - 屬性: <div my-directive="exp"> </div>
C - 類名:<div class="my-directive: exp;"></div>
M - 註釋: <!-- directive: my-directive exp -->
模板template - 將當前的元素替換掉。 這個替換過程會自動將元素的屬性和css類名添加到新元素上。更多細節請查考章節「建立widgets」。
模板templateUrl - 和template屬性同樣,只不過這裏指示的是一個模板的URL。由於模板加載是異步的,全部編譯和連接都會等到加載完成後再執行。
替換replace - 若是被設置成true那麼如今的元素會被模板替換,而不是被插入到元素中。
編譯模板transclude - 將元素編譯好,使得指令能夠開始使用它。通常狀況下須要和ngTransclude指令一塊兒使用。 使用嵌入的好處在於連接好書能夠獲取到預綁定在做用域上的函數。在一個典型的初始化過程當中,widget會建立一個孤立的做用域,可是嵌入並非其中一個子成員,而是這孤立做用域的兄弟成員。這使得widget能夠有一個私有的狀態,而且嵌入被綁定在父做用於上。
true - 嵌入指令的內容。
'element' - 嵌入整個元素,包括優先級較低的指令。
編譯compile - 這就是後面將要講到的編譯函數。
連接link - 這就是後面將要講到的連接函數。只有沒有提供編譯函數時纔會用到這個值。
指令這塊確實知識點比較多,也是angularJS中很重要的一部分。
全部的內置指令的前綴都爲ng,不建議自定義指令使用該前綴,以避免衝突。
首先從一些常見的內置指令開始。
先列出一些關鍵的內置指令,順便簡單說說做用域的問題。
ng-model
將表單控件和當前做用域的屬性進行綁定,這麼解釋彷佛也不太正確。
ng-init
該指令被調用時會初始化內部做用域。
ng-app
每一次用AngularJS都離不開這個指令,順便說下$rootScope。
聲明瞭ng-app的元素會成爲$rootScope的起點,而$rootScope是做用域鏈的根,一般聲明在<html>你懂的。
也就是說根下的做用域均可以訪問它。
可是,不建議過分使用$rootScope,省得全局變量滿天飛,效率又差又難管。
ng-controller
咱們用這個指令在一個DOM元素上裝上controller。
ng-disabled
像這種只要出現則生效的屬性,咱們能夠在AngularJS中經過表達式返回值true/false令其生效。
禁用表單輸入字段。
ng-readonly
經過表達式返回值true/false將表單輸入字段設爲只讀
ng-checked
標準的checked屬性是一個布爾屬性,不須要進行賦值。經過ng-checked將某個表達式同是出現checked屬性進行綁定。
ng-select
ng-select能夠對是否出現option標籤的selected屬性進行綁定。
ng-herf
當使用當前做用域中的屬性動態建立url時,應該用ng-href代替href
ng-src
angularJs會告訴瀏覽器呢在ng-src對應的表達式生效以前不要僞裝圖像。
ng-include
使用ng-include能夠加載、編譯包括外部HTML片斷到當前的應用中。
ng中的事件綁定相關指令
ng-change ng-click ng-dbclick ng-mouseenter ng-mouseleave ng-mousemove ng-mouseover ng-mouseup