Angular中實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)視圖實(shí)例代碼
近兩年當(dāng)中使用Angular開(kāi)發(fā)過(guò)很多項(xiàng)目,其中也涉及到一些樹(shù)形結(jié)構(gòu)視圖的顯示,最近的在項(xiàng)目中封裝了大量的組件,一些組件也有樹(shù)形結(jié)構(gòu)的展示,所以寫(xiě)出來(lái)總結(jié)一下。
相信大家都知道,樹(shù)結(jié)構(gòu)最典型的例子就是目錄結(jié)構(gòu)了吧,一個(gè)目錄可以包含很多子目錄,子目錄又可以包含若干個(gè)子孫目錄,那咱們今天就以目錄結(jié)構(gòu)為例來(lái)說(shuō)明一下Angular中樹(shù)結(jié)構(gòu)的實(shí)現(xiàn)。
首先,我們希望封裝一個(gè)組件,用于顯示整個(gè)目錄的樹(shù)形機(jī)構(gòu),代碼如下:
<!DOCTYPE html> <html ng-app="treeDemo"> <body> <div ng-controller="TreeController"> <folder-tree current-folder="folder"></folder-tree> </div> <script src="node_modules/angular/angular.min.js"></script> <script src="js/app.js"></script> </body> </html>
就像上面的代碼一樣,我們直接聲明folder-tree標(biāo)簽,將controller中的folder數(shù)據(jù)作為參數(shù)傳遞進(jìn)去,預(yù)期會(huì)顯示出一個(gè)完整的樹(shù)狀目錄結(jié)構(gòu)。接下來(lái)我們就來(lái)定義模塊,控制器,以及相應(yīng)的指令部分:
angular.module('treeDemo', []) .controller("TreeController", function($scope) { $scope.folder = { name: 'techs', children: [ { name: 'server-side', children: [ { name: 'Java' }, { name: 'Python' }, { name: 'Node' } ] }, { name: 'front-end', children: [ { name: 'jQuery' }, { name: 'Angular' }, { name: 'React' } ] } ] } }) .directive("folderTree", function() { return { restrict: "E", scope: { currentFolder: '=' }, templateUrl: 'template/tree.html' }; });
如上所述,在控制器中我們?cè)?scope中綁定了一個(gè)folder的數(shù)據(jù)對(duì)象,包含整個(gè)的目錄結(jié)構(gòu)數(shù)據(jù),接著定義了folderTree指令,它會(huì)使用tree.html作為模板進(jìn)行視圖渲染,我們這就來(lái)look look模板中的內(nèi)容:
<p>{{currentFolder.name}}</p> <ul> <li ng-repeat="subfolder in currentFolder.children"> <folder-tree current-folder="subfolder"></folder-tree> </li> </ul>
可以看到,在模板中有個(gè)很重要的環(huán)節(jié),那就是使用ng-repeat指令遍歷當(dāng)前目錄的子集,然后再次使用folder-tree組件來(lái)渲染相應(yīng)的子目錄,這種方式簡(jiǎn)直是完美,現(xiàn)在我們來(lái)看看渲染的結(jié)果:
這種在模板中嵌套使用指令的方法很完美,只可惜低版本的Angular中是無(wú)法實(shí)現(xiàn)的,會(huì)出現(xiàn)無(wú)限遞歸循環(huán),造成頁(yè)面假死,這是Angular本身的解析機(jī)制造成的。經(jīng)測(cè)試,Angular 1.5.0及以上版本是沒(méi)有問(wèn)題的,但在Angular 1.4.9及以下版本中就會(huì)運(yùn)行失敗。假如你在項(xiàng)目中真的使用了低版本的Angular并且造成運(yùn)行失敗,我們還可以稍微改動(dòng)一下模板,采用ng-include來(lái)實(shí)現(xiàn)同樣的功能:
<p>{{currentFolder.name}}</p> <ul> <li ng-repeat="subfolder in currentFolder.children" ng-include="'template/tree.html'" ng-init="currentFolder = subfolder"> </li> </ul>
我們?cè)谀0逯腥サ袅薴older-tree指令,使用了ng-include指令,再次將tree.html模板引入進(jìn)來(lái),需要注意的是,因?yàn)閚g-include會(huì)創(chuàng)建一個(gè)獨(dú)立的作用域,為了讓其正確的引用到currentFolder變量,我們需要在每個(gè)ng-include中初始化currentFolder,將其賦值為ng-repeat中的當(dāng)前subfolder,另外,別忘了ng-include指令中模板路徑前后加上單引號(hào)。
這樣確實(shí)可以,但是你可能覺(jué)得有些遺憾,沒(méi)能使用最好的解決方案來(lái)實(shí)現(xiàn)這個(gè)樹(shù)結(jié)構(gòu)。
其實(shí)這個(gè)問(wèn)題早就有人吐槽了,令人驚喜的是,有個(gè)叫Mark的伙計(jì)寫(xiě)了一個(gè)service專(zhuān)門(mén)解決這個(gè)問(wèn)題:
/* * An Angular service which helps with creating recursive directives. * @author Mark Lagendijk * @license MIT */ angular.module('RecursionHelper', []).factory('RecursionHelper', ['$compile', function($compile){ return { /** * Manually compiles the element, fixing the recursion loop. * @param element * @param [link] A post-link function, or an object with function(s) registered via pre and post properties. * @returns An object containing the linking functions. */ compile: function(element, link){ // Normalize the link parameter // 如果link參數(shù)是對(duì)象類(lèi)型link:{pre: function(...){}, post: function(...){}}則不做處理 // 如果link參數(shù)是函數(shù)類(lèi)型則將其作為post-link函數(shù)在$compile之后調(diào)用 if(angular.isFunction(link)){ link = { post: link }; } // Break the recursion loop by removing the contents // 獲取并清空當(dāng)前元素的內(nèi)容,后面進(jìn)行編譯 var contents = element.contents().remove(); var compiledContents; return { pre: (link && link.pre) ? link.pre : null, /** * Compiles and re-adds the contents * 編譯和重新添加內(nèi)容到當(dāng)前元素 */ post: function(scope, element){ // Compile the contents if(!compiledContents){ compiledContents = $compile(contents); } // Re-add the compiled contents to the element compiledContents(scope, function(clone){ element.append(clone); }); // Call the post-linking function, if any if(link && link.post){ link.post.apply(null, arguments); } } }; } }; }]);
現(xiàn)在我們只需引入這個(gè)模塊,然后在directive中使用RecursionHelper這個(gè)service,調(diào)用其compile手動(dòng)編譯指令對(duì)應(yīng)的元素節(jié)點(diǎn):
angular.module('treeDemo', ['RecursionHelper']) .controller("TreeController", function($scope) { $scope.folder = { name: 'techs', children: [ { name: 'server-side', children: [ { name: 'Java' }, { name: 'Python' }, { name: 'Node' } ] }, { name: 'front-end', children: [ { name: 'jQuery' }, { name: 'Angular' }, { name: 'React' } ] } ] } }) .directive("folderTree", function(RecursionHelper) { return { restrict: "E", scope: { currentFolder: '=' }, templateUrl: 'template/tree.html', compile: function(element) { // 我們這里使用RecursionHelper的compile方法編譯指令當(dāng)前元素,這里第二個(gè)參數(shù)指定一個(gè)函數(shù),相當(dāng)于常用的link函數(shù) // 當(dāng)然我們也可以指定一個(gè)對(duì)象,里面包含pre和post函數(shù),分別對(duì)應(yīng)pre-link和post-link return RecursionHelper.compile(element, function(scope, iElement, iAttrs, controller, transcludeFn){ // Define your normal link function here. // Alternative: instead of passing a function, // you can also pass an object with // a 'pre'- and 'post'-link function. // 這里可以往scope中綁定一些變量 scope.variable = 'hello world'; }); } }; });
在上面代碼中,我們?cè)趧?chuàng)建treeDemo模塊時(shí)引入RecursionHelper模塊,然后在創(chuàng)建folderTree指令時(shí)使用RecursionHelper服務(wù),并且在compile方法中調(diào)用RecursionHelper的compile方法,即可修復(fù)上面的問(wèn)題。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Angular2.0/4.0 使用Echarts圖表的示例代碼
- 詳解angularjs實(shí)現(xiàn)echart圖表效果最簡(jiǎn)潔教程
- Angular實(shí)現(xiàn)svg和png圖片下載實(shí)現(xiàn)
- Angular.js與Bootstrap相結(jié)合實(shí)現(xiàn)表格分頁(yè)代碼
- 詳解AngularJS中的表格使用
- AngularJS實(shí)現(xiàn)表格的增刪改查(僅限前端)
- angularjs表格ng-table使用備忘錄
- 最棒的Angular2表格控件
- 使用angularjs創(chuàng)建簡(jiǎn)單表格
- Angular2使用SVG自定義圖表(條形圖、折線圖)組件示例
相關(guān)文章
AngularJS 自定義過(guò)濾器詳解及實(shí)例代碼
這篇文章主要介紹了AngularJS 自定義過(guò)濾器,這里整理了相關(guān)資料及示例代碼,有興趣的小伙伴可以參考下2016-09-09Angular實(shí)現(xiàn)雙向折疊列表組件的示例代碼
本篇文章主要介紹了Angular實(shí)現(xiàn)雙向折疊列表組件的示例代碼,分為左右兩組,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Angular項(xiàng)目如何使用攔截器?httpClient?請(qǐng)求響應(yīng)處理
這篇文章主要介紹了Angular項(xiàng)目簡(jiǎn)單使用攔截器httpClient請(qǐng)求響應(yīng)處理,目前我的Angular版本是Angular?17.3,版本中實(shí)現(xiàn)請(qǐng)求和響應(yīng)的攔截處理了,這種機(jī)制非常適合添加如身份驗(yàn)證頭、錯(cuò)誤統(tǒng)一處理、日志記錄等功能,需要的朋友可以參考下2024-06-06AngularJS中關(guān)于ng-class指令的幾種實(shí)現(xiàn)方式詳解
這篇文章給大家介紹了angularJS中ng-class指令的三種實(shí)現(xiàn)方式,其中包括通過(guò)數(shù)據(jù)的雙向綁定、通過(guò)對(duì)象數(shù)組和通過(guò)key/value這三種方式,有需要的朋友們可以參考學(xué)習(xí),下面來(lái)一起看看吧。2016-09-09angularjs 中$apply,$digest,$watch詳解
這篇文章主要介紹了angularjs 中$apply,$digest,$watch詳解的相關(guān)資料,需要的朋友可以參考下2016-10-10Angular企業(yè)級(jí)開(kāi)發(fā)——MVC之控制器詳解
本篇文章主要介紹了Angular企業(yè)級(jí)開(kāi)發(fā)——MVC之控制器詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-02-02AngularJS實(shí)現(xiàn)與后臺(tái)服務(wù)器進(jìn)行交互的示例講解
今天小編就為大家分享一篇AngularJS實(shí)現(xiàn)與后臺(tái)服務(wù)器進(jìn)行交互的示例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Angular懶加載模塊與Combined?Injector原理全面解析
這篇文章主要為大家介紹了Angular懶加載模塊與Combined?Injector原理全面解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10