Angular中實現(xiàn)樹形結(jié)構(gòu)視圖實例代碼
近兩年當(dāng)中使用Angular開發(fā)過很多項目,其中也涉及到一些樹形結(jié)構(gòu)視圖的顯示,最近的在項目中封裝了大量的組件,一些組件也有樹形結(jié)構(gòu)的展示,所以寫出來總結(jié)一下。
相信大家都知道,樹結(jié)構(gòu)最典型的例子就是目錄結(jié)構(gòu)了吧,一個目錄可以包含很多子目錄,子目錄又可以包含若干個子孫目錄,那咱們今天就以目錄結(jié)構(gòu)為例來說明一下Angular中樹結(jié)構(gòu)的實現(xiàn)。
首先,我們希望封裝一個組件,用于顯示整個目錄的樹形機構(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ù)傳遞進去,預(yù)期會顯示出一個完整的樹狀目錄結(jié)構(gòu)。接下來我們就來定義模塊,控制器,以及相應(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'
};
});
如上所述,在控制器中我們在$scope中綁定了一個folder的數(shù)據(jù)對象,包含整個的目錄結(jié)構(gòu)數(shù)據(jù),接著定義了folderTree指令,它會使用tree.html作為模板進行視圖渲染,我們這就來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>
可以看到,在模板中有個很重要的環(huán)節(jié),那就是使用ng-repeat指令遍歷當(dāng)前目錄的子集,然后再次使用folder-tree組件來渲染相應(yīng)的子目錄,這種方式簡直是完美,現(xiàn)在我們來看看渲染的結(jié)果:

這種在模板中嵌套使用指令的方法很完美,只可惜低版本的Angular中是無法實現(xiàn)的,會出現(xiàn)無限遞歸循環(huán),造成頁面假死,這是Angular本身的解析機制造成的。經(jīng)測試,Angular 1.5.0及以上版本是沒有問題的,但在Angular 1.4.9及以下版本中就會運行失敗。假如你在項目中真的使用了低版本的Angular并且造成運行失敗,我們還可以稍微改動一下模板,采用ng-include來實現(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>
我們在模板中去掉了folder-tree指令,使用了ng-include指令,再次將tree.html模板引入進來,需要注意的是,因為ng-include會創(chuàng)建一個獨立的作用域,為了讓其正確的引用到currentFolder變量,我們需要在每個ng-include中初始化currentFolder,將其賦值為ng-repeat中的當(dāng)前subfolder,另外,別忘了ng-include指令中模板路徑前后加上單引號。
這樣確實可以,但是你可能覺得有些遺憾,沒能使用最好的解決方案來實現(xiàn)這個樹結(jié)構(gòu)。
其實這個問題早就有人吐槽了,令人驚喜的是,有個叫Mark的伙計寫了一個service專門解決這個問題:
/*
* 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ù)是對象類型link:{pre: function(...){}, post: function(...){}}則不做處理
// 如果link參數(shù)是函數(shù)類型則將其作為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)容,后面進行編譯
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)在我們只需引入這個模塊,然后在directive中使用RecursionHelper這個service,調(diào)用其compile手動編譯指令對應(yīng)的元素節(jié)點:
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)前元素,這里第二個參數(shù)指定一個函數(shù),相當(dāng)于常用的link函數(shù)
// 當(dāng)然我們也可以指定一個對象,里面包含pre和post函數(shù),分別對應(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';
});
}
};
});
在上面代碼中,我們在創(chuàng)建treeDemo模塊時引入RecursionHelper模塊,然后在創(chuàng)建folderTree指令時使用RecursionHelper服務(wù),并且在compile方法中調(diào)用RecursionHelper的compile方法,即可修復(fù)上面的問題。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Angular利用內(nèi)容投射向組件輸入ngForOf模板的方法
本篇文章主要介紹了Angular利用內(nèi)容投射向組件輸入ngForOf模板的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
AngularJS服務(wù)service用法總結(jié)
這篇文章主要介紹了AngularJS服務(wù)service用法,結(jié)合實例形式總結(jié)分析了服務(wù)Service的概念、功能及自定義服務(wù)的相關(guān)操作技巧,需要的朋友可以參考下2016-12-12
AngularJs ng-repeat 嵌套如何獲取外層$index
這篇文章主要介紹了AngularJs ng-repeat 嵌套如何獲取外層$index的相關(guān)資料,需要的朋友可以參考下2016-09-09
angularjs結(jié)合html5實現(xiàn)拖拽功能
本篇文章給大家分享了angularjs結(jié)合html5實現(xiàn)拖拽功能的方法以及代碼實例,有興趣的朋友參考下。2018-06-06
AngularJs Understanding Angular Templates
本文主要介紹AngularJs Understanding Angular Templates的資料,這里整理了詳細(xì)的資料及簡單示例代碼,有興趣的小伙伴的參考下2016-09-09
分享使用AngularJS創(chuàng)建應(yīng)用的5個框架
如果你計劃使用AngularJS創(chuàng)建你的Web應(yīng)用,那現(xiàn)在就開始吧。你不需要有任何的恐懼和擔(dān)心,因為現(xiàn)在有很多的框架都可以很好地支持AngularJS2015-12-12
Webpack 實現(xiàn) AngularJS 的延遲加載
這篇文章主要介紹了Webpack 實現(xiàn) AngularJS 的延遲加載的相關(guān)資料,需要的朋友可以參考下2016-03-03

