您好,登錄后才能下訂單哦!
前言
在我們創建一個angularJS應用的時候,菜單往往往是不可或缺的元素之一。也許在我們靜態菜單的時候不會發現在指令中操作菜單收縮、折疊展開沒有任何問題,因為我們在操作之前,頁面元素渲染已經完成,所以在指令里面通過element查找目標元素可以成功。但是一旦我們的菜單的數據不是靜態而是通過后臺接口加載動態數據渲染,我們會發現本來在靜態寫好的指令操作,在轉變為動態數據加載之后,怎么也沒法查找到想要的目標元素。
遇到如此問題,開始覺得好奇葩的,當然這也是吐槽一下,還是得好好解決問題的,痛定失痛,決心好好理清思路,分析一下問題原因。首先我們先了解一下AngularJS的生命周期。
AngularJS的生命周期
在AngularJS應用啟動前,它們會以HTML文本的形式保存在文本編輯器中。應用啟動后會進行編譯和鏈接,作用域會同HTML進行綁定,應用可以對用戶在HTML中進行的操作進行實時響應。AngularJS的生命周期主要有兩個主要階段:一個是編譯階段,一個是鏈接階段。
AngularJS生命周期-編譯階段
在編譯階段,AngularJS會遍歷整個HTML文檔并根據JavaScript中的指令定義來處理頁面上聲明的指令。每一個指令模板中可能有另一個指令,另一個指令也有可能會有自己的模板。AngularJS調用HTML文檔根部的指令時,會遍歷其中所有的模板,模板中可能含有模板的指令。如果一個元素已經有一個含有模板的指令,永遠不要對其用另一個指令進行修飾,只有最高優先級的指令中的模板會被編譯。
一旦對指令和其中的子模板進行遍歷或編譯,編譯后的模板會返回一個叫做模板函數的函數。在這個時候的DOM樹還沒有進行數據綁定,此時對DOM樹操作只會有很少的性能開銷,ng-repeat和ng-transclude等內置指令會在這個時候對還未進行數據綁定的DOM進行操作。比如ng-repeat,它會遍歷指定的數組或對象,在數據綁定之前構建對應的DOM結構,然后將新的DOM(編譯后的DOM)傳遞給指令生命周期中的下一階段,鏈接階段。一個指令的DOM一旦編譯完成,就可以立即通過編譯函數對其進行訪問,編譯函數的簽名包含有訪問指令聲明所在的元素(tElements)及該元素對其他屬性(tAttrs)的方法。
compile返回對象或函數,compile()函數負責對模板DOM進行轉換,link()函數負責將作用域和DOM進行轉換。
//... compile: function(tEle,tAttrs,transcludeFn){ var tplEl = angular.element('<div>' +'<h3></h3>'+'</div>'); var h3 = tplEl.find('h3'); h3.attr('type',tAttrs.type); h3.attr('ng-model',tAttrs.ngModel); h3.val('hello'); tEle.replaceWith(tplEl); return function(scope, ele, attrs){ //連接函數 }; } //...
AngularJS生命周期-鏈接階段
link函數創建可以操作DOM的指令,鏈接函數是可選的。定義了編譯函數,返回鏈接函數,當兩個函數都定義了,編譯函數會重載鏈接函數。
//下面2種定義指令的放松在功能上是完全一樣的 angular.module('myApp',[]) .directive('myDirective', function (){ return { pre: function (tElement, tAttrs, transclude){ //在子元素被鏈接之前執行,之后調用‘link'函數無法定位鏈接的元素 }, post: function (scope, iElement, iAttrs, controllers){ //在子元素被鏈接之后執行 } } });
angular.module('myApp',[]) .directive('myDirective', function (){ return { link: function (scope, ele, attrs){ return { pre: function (tElement, tAttrs, transclude){ //在子元素被鏈接之前執行,之后調用‘link'函數無法定位鏈接的元素 }, post: function (scope, iElement, iAttrs, controllers){ //在子元素被鏈接之后執行 } } } } });
當定義了編譯函數來取代鏈接函數時,鏈接函數使我們能提供給返回對象的第二個方法,也就是postLink函數。鏈接函數會在模板編譯并同作用域進行鏈接后被調用,它負責設置事件監聽器,監聽數據變化和實時的操作DOM。
//鏈接函數簽名 link: function(scope, element, attrs){ //操作DOM } //含require選項, require someContainer link: function(scope, element, attrs, someContainer){ //在這里操作DOM,可以訪問require指定的控制器 }
控制器在所有的指令間共享,因此指令可以將控制器當作通信通道(公共API),如果設置多個require,這個參數是一個控制器實例組成的數組,而不是一個單獨的控制器。
問題剖析
在通過對AngularJS生命周期的理解,我們可以清晰地認識到動態菜單為什么綁定在鏈接階段上的DOM操作沒有成功,由于ng-repeat的原因,我對DOM樹操作沒找到DOM元素。因為在封裝成一個菜單指令組件的時候,我內部的菜單數據加載使用ng-repeat實現,所以只有在這個時候才能在ng-repeat內部綁定對DOM樹的操作。
最初的寫法:
//html <menu-bar> ````` <div ng-repeat="ml in menuLists"> `````` <div ng-repeat="mls in ml.secondLists"> `````` <div ng-repeat="mlt in mls.thirdLists"> `````` </div> `````` </div> `````` </div> `````` </menu-bar> //directive angular.module('',[]).directive('menuBar',function (){ return { restrict: 'E', replace: true, link: function (scope, element, attr){ //操作菜單的邏輯代碼 } } });
這種寫法,在link里面操作菜單邏輯的代碼沒有被觸發,尼瑪,angularjs的檢測機制也沒用,因為ng-repeat的原因導致DOM操作事件沒有被掛載到DOM上,所以想操作菜單不可能成功。但是,如果ng-repeat的內容是靜態存在的,link函數里面的操作是可以實現的。
修改后的寫法:
//html <div ng-repeat="ml in menuLists"> `````` <div ng-repeat="mls in ml.secondLists"> `````` <menu-bar> `````` <div ng-repeat="mlt in mls.thirdLists"> `````` <menu-bar> `````` </menu-bar> `````` </div> `````` </menu-bar> `````` </div> `````` </div> //directive angular.module('',[]).directive('menuBar',function (){ return { restrict: 'E', replace: true, link: function (scope, element, attr){ //操作菜單的邏輯代碼 } } });
修改之后我們將我們操作動態加載的DOM結構的指令放入ng-repeat中,此時邏輯正常執行,在link函數中能打印出DOM結構。
以上所述是小編給大家介紹的AngularJS動態菜單操作指令,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。