前言
10余年的平頂山網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷(xiāo)型網(wǎng)站的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整平頂山建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“平頂山網(wǎng)站設(shè)計(jì)”,“平頂山網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
在我們創(chuàng)建一個(gè)angularJS應(yīng)用的時(shí)候,菜單往往往是不可或缺的元素之一。也許在我們靜態(tài)菜單的時(shí)候不會(huì)發(fā)現(xiàn)在指令中操作菜單收縮、折疊展開(kāi)沒(méi)有任何問(wèn)題,因?yàn)槲覀冊(cè)诓僮髦?,?yè)面元素渲染已經(jīng)完成,所以在指令里面通過(guò)element查找目標(biāo)元素可以成功。但是一旦我們的菜單的數(shù)據(jù)不是靜態(tài)而是通過(guò)后臺(tái)接口加載動(dòng)態(tài)數(shù)據(jù)渲染,我們會(huì)發(fā)現(xiàn)本來(lái)在靜態(tài)寫(xiě)好的指令操作,在轉(zhuǎn)變?yōu)閯?dòng)態(tài)數(shù)據(jù)加載之后,怎么也沒(méi)法查找到想要的目標(biāo)元素。
遇到如此問(wèn)題,開(kāi)始覺(jué)得好奇葩的,當(dāng)然這也是吐槽一下,還是得好好解決問(wèn)題的,痛定失痛,決心好好理清思路,分析一下問(wèn)題原因。首先我們先了解一下AngularJS的生命周期。
AngularJS的生命周期
在AngularJS應(yīng)用啟動(dòng)前,它們會(huì)以HTML文本的形式保存在文本編輯器中。應(yīng)用啟動(dòng)后會(huì)進(jìn)行編譯和鏈接,作用域會(huì)同HTML進(jìn)行綁定,應(yīng)用可以對(duì)用戶在HTML中進(jìn)行的操作進(jìn)行實(shí)時(shí)響應(yīng)。AngularJS的生命周期主要有兩個(gè)主要階段:一個(gè)是編譯階段,一個(gè)是鏈接階段。
AngularJS生命周期-編譯階段
在編譯階段,AngularJS會(huì)遍歷整個(gè)HTML文檔并根據(jù)JavaScript中的指令定義來(lái)處理頁(yè)面上聲明的指令。每一個(gè)指令模板中可能有另一個(gè)指令,另一個(gè)指令也有可能會(huì)有自己的模板。AngularJS調(diào)用HTML文檔根部的指令時(shí),會(huì)遍歷其中所有的模板,模板中可能含有模板的指令。如果一個(gè)元素已經(jīng)有一個(gè)含有模板的指令,永遠(yuǎn)不要對(duì)其用另一個(gè)指令進(jìn)行修飾,只有最高優(yōu)先級(jí)的指令中的模板會(huì)被編譯。
一旦對(duì)指令和其中的子模板進(jìn)行遍歷或編譯,編譯后的模板會(huì)返回一個(gè)叫做模板函數(shù)的函數(shù)。在這個(gè)時(shí)候的DOM樹(shù)還沒(méi)有進(jìn)行數(shù)據(jù)綁定,此時(shí)對(duì)DOM樹(shù)操作只會(huì)有很少的性能開(kāi)銷(xiāo),ng-repeat和ng-transclude等內(nèi)置指令會(huì)在這個(gè)時(shí)候?qū)€未進(jìn)行數(shù)據(jù)綁定的DOM進(jìn)行操作。比如ng-repeat,它會(huì)遍歷指定的數(shù)組或?qū)ο?,在?shù)據(jù)綁定之前構(gòu)建對(duì)應(yīng)的DOM結(jié)構(gòu),然后將新的DOM(編譯后的DOM)傳遞給指令生命周期中的下一階段,鏈接階段。一個(gè)指令的DOM一旦編譯完成,就可以立即通過(guò)編譯函數(shù)對(duì)其進(jìn)行訪問(wèn),編譯函數(shù)的簽名包含有訪問(wèn)指令聲明所在的元素(tElements)及該元素對(duì)其他屬性(tAttrs)的方法。
compile返回對(duì)象或函數(shù),compile()函數(shù)負(fù)責(zé)對(duì)模板DOM進(jìn)行轉(zhuǎn)換,link()函數(shù)負(fù)責(zé)將作用域和DOM進(jìn)行轉(zhuǎn)換。
//... compile: function(tEle,tAttrs,transcludeFn){ var tplEl = angular.element('' +''+''); 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){ //連接函數(shù) }; } //...
AngularJS生命周期-鏈接階段
link函數(shù)創(chuàng)建可以操作DOM的指令,鏈接函數(shù)是可選的。定義了編譯函數(shù),返回鏈接函數(shù),當(dāng)兩個(gè)函數(shù)都定義了,編譯函數(shù)會(huì)重載鏈接函數(shù)。
//下面2種定義指令的放松在功能上是完全一樣的 angular.module('myApp',[]) .directive('myDirective', function (){ return { pre: function (tElement, tAttrs, transclude){ //在子元素被鏈接之前執(zhí)行,之后調(diào)用‘link'函數(shù)無(wú)法定位鏈接的元素 }, post: function (scope, iElement, iAttrs, controllers){ //在子元素被鏈接之后執(zhí)行 } } });
angular.module('myApp',[]) .directive('myDirective', function (){ return { link: function (scope, ele, attrs){ return { pre: function (tElement, tAttrs, transclude){ //在子元素被鏈接之前執(zhí)行,之后調(diào)用‘link'函數(shù)無(wú)法定位鏈接的元素 }, post: function (scope, iElement, iAttrs, controllers){ //在子元素被鏈接之后執(zhí)行 } } } } });
當(dāng)定義了編譯函數(shù)來(lái)取代鏈接函數(shù)時(shí),鏈接函數(shù)使我們能提供給返回對(duì)象的第二個(gè)方法,也就是postLink函數(shù)。鏈接函數(shù)會(huì)在模板編譯并同作用域進(jìn)行鏈接后被調(diào)用,它負(fù)責(zé)設(shè)置事件監(jiān)聽(tīng)器,監(jiān)聽(tīng)數(shù)據(jù)變化和實(shí)時(shí)的操作DOM。
//鏈接函數(shù)簽名 link: function(scope, element, attrs){ //操作DOM } //含require選項(xiàng), require someContainer link: function(scope, element, attrs, someContainer){ //在這里操作DOM,可以訪問(wèn)require指定的控制器 }
控制器在所有的指令間共享,因此指令可以將控制器當(dāng)作通信通道(公共API),如果設(shè)置多個(gè)require,這個(gè)參數(shù)是一個(gè)控制器實(shí)例組成的數(shù)組,而不是一個(gè)單獨(dú)的控制器。
問(wèn)題剖析
在通過(guò)對(duì)AngularJS生命周期的理解,我們可以清晰地認(rèn)識(shí)到動(dòng)態(tài)菜單為什么綁定在鏈接階段上的DOM操作沒(méi)有成功,由于ng-repeat的原因,我對(duì)DOM樹(shù)操作沒(méi)找到DOM元素。因?yàn)樵诜庋b成一個(gè)菜單指令組件的時(shí)候,我內(nèi)部的菜單數(shù)據(jù)加載使用ng-repeat實(shí)現(xiàn),所以只有在這個(gè)時(shí)候才能在ng-repeat內(nèi)部綁定對(duì)DOM樹(shù)的操作。
最初的寫(xiě)法:
//html````` //directive angular.module('',[]).directive('menuBar',function (){ return { restrict: 'E', replace: true, link: function (scope, element, attr){ //操作菜單的邏輯代碼 } } });````````````````````````````````````
這種寫(xiě)法,在link里面操作菜單邏輯的代碼沒(méi)有被觸發(fā),尼瑪,angularjs的檢測(cè)機(jī)制也沒(méi)用,因?yàn)閚g-repeat的原因?qū)е翫OM操作事件沒(méi)有被掛載到DOM上,所以想操作菜單不可能成功。但是,如果ng-repeat的內(nèi)容是靜態(tài)存在的,link函數(shù)里面的操作是可以實(shí)現(xiàn)的。
修改后的寫(xiě)法:
//html``````//directive angular.module('',[]).directive('menuBar',function (){ return { restrict: 'E', replace: true, link: function (scope, element, attr){ //操作菜單的邏輯代碼 } } });`````````````````` ```````````````````````` ``````
修改之后我們將我們操作動(dòng)態(tài)加載的DOM結(jié)構(gòu)的指令放入ng-repeat中,此時(shí)邏輯正常執(zhí)行,在link函數(shù)中能打印出DOM結(jié)構(gòu)。
以上所述是小編給大家介紹的AngularJS動(dòng)態(tài)菜單操作指令,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)創(chuàng)新互聯(lián)網(wǎng)站的支持!