前言:最近產品需要設計一套相對完整的組織架構的解決方案,由於組織架構涉及到層級關系,在表格里面展示層級關系,自然就要用到所謂的treegrid。可惜的是,一些輕量級的表格組件本身並沒有自帶樹形表格的功能,比如bootstrapTable就沒有這個功能,怎么辦呢?如果是jqgrid、easyUI的表格,treegrid的效果可以說是輕而易舉就能解決,而項目目前用的就是bootstrapTable,不可能這個時候因為這個需求去換組件吧。博主分析了下,無非就兩種解決方案:一種就是擴展bootstrapTable的treegrid功能;第二種就是再找一個單獨的treegrid組件去實現這個功能。博主在網上找了下,找到了一個效果還不錯的treegrid第三方組件,於是做了下封裝,今天分享出來,供大家參考。
本文原創地址:http://www.cnblogs.com/landeanfen/p/6776152.html
一、開源的treegrid
1、組件效果預覽
最原始的效果

bootstrap樣式的效果


這個是組件最原始的效果,后面會告訴大家博主做了哪些封裝以及加了哪些功能。
在此還是給出一個封裝過的效果吧!

2、組件開源地址
最后還是給出github上面一個開源的treegrid組件。
github開源地址:https://github.com/maxazan/jquery-treegrid
文檔示例地址:http://maxazan.github.io/jquery-treegrid/
bootstrap樣式的demo以及使用:http://maxazan.github.io/jquery-treegrid/examples/example-bootstrap-3.html
二、封裝treegrid
1、組件封裝的必要性
(1)縱觀組件的所有的demo和文檔,基本都是說的我們直接寫死的table標簽,然后通過樣式去確定父子關系,最后初始化得到效果,但大部分情況下,我們的表格數據都不是寫死的,而是通過后台獲取數據,然后將數據渲染到前端,最終得到我們想要的效果,如果根據組件目前的使用方式,我們得到一個集合數據之后,需要自己去拼接tr、td這些東西,這都是小事,最麻煩的是組件是有父子關系的,我們需要根據我們數據之間的關系轉化為組件的父子關系,並且由於支持無限級,還涉及到數據的遞歸運算。這個復雜的過程是我們不想經常去做的,怎么辦呢?最好的思路就是封裝了,封裝的時候麻煩一次,以后使用就簡單了,可以說這是一件一勞永逸的事情。
(2)一般來說,既然是treegrid,肯定會有表頭,而這個表頭是根據數據來動態顯示的。組件自帶的效果可以自己寫死表頭,但還是那句話,使用的靈活性太差。
由於以上兩點,於是才有了今天的這篇文章。
2、組件封裝代碼示例
首先我們將treegrid組件下載並引用到我們的項目里面,然后向其目錄里面加一個extension的文件夾,里面添加一個jquery.treegrid.extension.js的文件。

然后就是最重要的jquery.treegrid.extension.js文件的內容:
(function ($) {
"use strict";
$.fn.treegridData = function (options, param) {
//如果是調用方法
if (typeof options == 'string') {
return $.fn.treegridData.methods[options](this, param);
}
//如果是初始化組件
options = $.extend({}, $.fn.treegridData.defaults, options || {});
var target = $(this);
debugger;
//得到根節點
target.getRootNodes = function (data) {
var result = [];
$.each(data, function (index, item) {
if (!item[options.parentColumn]) {
result.push(item);
}
});
return result;
};
var j = 0;
//遞歸獲取子節點並且設置子節點
target.getChildNodes = function (data, parentNode, parentIndex, tbody) {
$.each(data, function (i, item) {
if (item[options.parentColumn] == parentNode[options.id]) {
var tr = $('<tr></tr>');
var nowParentIndex = (parentIndex + (j++) + 1);
tr.addClass('treegrid-' + nowParentIndex);
tr.addClass('treegrid-parent-' + parentIndex);
$.each(options.columns, function (index, column) {
var td = $('<td></td>');
td.text(item[column.field]);
tr.append(td);
});
tbody.append(tr);
target.getChildNodes(data, item, nowParentIndex, tbody)
}
});
};
target.addClass('table');
if (options.striped) {
target.addClass('table-striped');
}
if (options.bordered) {
target.addClass('table-bordered');
}
if (options.url) {
$.ajax({
type: options.type,
url: options.url,
data: options.ajaxParams,
dataType: "JSON",
success: function (data, textStatus, jqXHR) {
debugger;
//構造表頭
var thr = $('<tr></tr>');
$.each(options.columns, function (i, item) {
var th = $('<th style="padding:10px;"></th>');
th.text(item.title);
thr.append(th);
});
var thead = $('<thead></thead>');
thead.append(thr);
target.append(thead);
//構造表體
var tbody = $('<tbody></tbody>');
var rootNode = target.getRootNodes(data);
$.each(rootNode, function (i, item) {
var tr = $('<tr></tr>');
tr.addClass('treegrid-' + (j + i));
$.each(options.columns, function (index, column) {
var td = $('<td></td>');
td.text(item[column.field]);
tr.append(td);
});
tbody.append(tr);
target.getChildNodes(data, item, (j + i), tbody);
});
target.append(tbody);
target.treegrid({
expanderExpandedClass: options.expanderExpandedClass,
expanderCollapsedClass: options.expanderCollapsedClass
});
if (!options.expandAll) {
target.treegrid('collapseAll');
}
}
});
}
else {
//也可以通過defaults里面的data屬性通過傳遞一個數據集合進來對組件進行初始化....有興趣可以自己實現,思路和上述類似
}
return target;
};
$.fn.treegridData.methods = {
getAllNodes: function (target, data) {
return target.treegrid('getAllNodes');
},
//組件的其他方法也可以進行類似封裝........
};
$.fn.treegridData.defaults = {
id: 'Id',
parentColumn: 'ParentId',
data: [], //構造table的數據集合
type: "GET", //請求數據的ajax類型
url: null, //請求數據的ajax的url
ajaxParams: {}, //請求數據的ajax的data屬性
expandColumn: null,//在哪一列上面顯示展開按鈕
expandAll: true, //是否全部展開
striped: false, //是否各行漸變色
bordered: false, //是否顯示邊框
columns: [],
expanderExpandedClass: 'glyphicon glyphicon-chevron-down',//展開的按鈕的圖標
expanderCollapsedClass: 'glyphicon glyphicon-chevron-right'//縮起的按鈕的圖標
};
})(jQuery);
代碼說明
1、為了避免和源組件的初始化沖突,我們自定義的組件取了一個別名,叫 treegridData 。我們使用組件的時候就通過treegridData來進行初始化,如果你覺得這個名稱不順眼,可以自行修改。
2、代碼的封裝思路基本是參考博主之前介紹組件的封裝 http://www.cnblogs.com/landeanfen/p/5124542.html這一篇里面的內容來的。
3、defaults里面就是初始化組件的時候可以傳遞的參數,上述注釋基本上寫得比較清楚。id和parentId兩個參數主要是用來描述數據之間的父子級關系,后面我們介紹組件時候的時候你一看就能明白。
4、博主加了幾個自認為很有用的屬性和方法,應該能減少一些使用的麻煩。比如初始化組件的時候是否展開所有的子節點、添加title、表格行的漸變色和表格邊框等。
5、上述封裝里面遞歸查找子節點的時候,每一次都需要遍歷所有的數據去找子節點,效率偏低,如果你使用了類似linq to js之類的組件去操作js的集合,可以優化那部分代碼,適當提高遞歸的效率。當然,如果你的結果集本身數據量不太大,這么寫影響也不太大。
3、封裝后的組件使用
我們在界面上面引用需要的css和js文件
<link href="~/Content/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/jquery-treegrid-master/css/jquery.treegrid.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Content/bootstrap/js/bootstrap.min.js"></script>
<script src="~/Content/jquery-treegrid-master/js/jquery.treegrid.min.js"></script>
<script src="~/Content/jquery-treegrid-master/js/jquery.treegrid.bootstrap3.js"></script>
<script src="~/Content/jquery-treegrid-master/extension/jquery.treegrid.extension.js"></script>
然后定義一個空的table標簽
<table id="tb" ></table>
最后就是js初始化了
$(document).ready(function () {
$('#tb').treegridData({
id: 'Id',
parentColumn: 'ParentId',
type: "GET", //請求數據的ajax類型
url: '/TestMVC/GetData', //請求數據的ajax的url
ajaxParams: {}, //請求數據的ajax的data屬性
expandColumn: null,//在哪一列上面顯示展開按鈕
striped: true, //是否各行漸變色
bordered: true, //是否顯示邊框
//expandAll: false, //是否全部展開
columns: [
{
title: '機構名稱',
field: 'Name'
},
{
title: '機構描述',
field: 'Desc'
}
]
});
});
當然啦,還得配上后台的取數據的方法
public class TestMVCController : Controller
{public JsonResult GetData()
{
var result = new List<object>();
result.Add(new { Id = 1, Name = "百度科技", Desc = "搜索巨頭"});
result.Add(new { Id = 2, Name = "百度事業部", Desc = "搜索巨頭",ParentId=1 });
result.Add(new { Id = 3, Name = "百度人事部", Desc = "搜索巨頭", ParentId = 1 });
result.Add(new { Id = 11, Name = "百度HH部", Desc = "搜索巨頭", ParentId = 2 });
result.Add(new { Id = 4, Name = "百度行政", Desc = "搜索巨頭", ParentId = 1 });
result.Add(new { Id = 5, Name = "百度YY部", Desc = "搜索巨頭", ParentId = 1 });
result.Add(new { Id = 12, Name = "百度BB部", Desc = "搜索巨頭", ParentId = 2 });
result.Add(new { Id = 6, Name = "搜狐科技", Desc = "IT" });
result.Add(new { Id = 7, Name = "搜狐信息部", Desc = "IT", ParentId = 6 });
result.Add(new { Id = 8, Name = "搜狐人事", Desc = "IT", ParentId = 6 });
result.Add(new { Id = 9, Name = "搜狐事業部", Desc = "IT", ParentId = 6 });
result.Add(new { Id = 10, Name = "搜狐事業子部", Desc = "IT", ParentId = 9 });
return Json(result, JsonRequestBehavior.AllowGet);
}
}
這里一看應該就能明白組件defaults里面的id和parentColumn的作用了吧。記得jqgrid里面使用treeview的時候用到了一個level用來判斷是哪一級別的節點,博主覺得這樣硬性要求返回數據里面加一個level屬性有點不妥,所以我們約定如果當前記錄的parentId為null或者空字符串的時候,這個節點就是根節點,然后根據根節點去遞歸找子節點。
使用后的各種效果示例如下。
初始化的時候配置expandAll: false得到的效果

增加隔行變色striped: true

增加表格邊框bordered: true

綜合效果

三、總結
至此本文就結束了,沒有什么太高大上的技術,就是簡單將一個第三方組件進行了一些封裝,使得其使用起來更加方便而已。如果你項目中也正在為treegrid而糾結,何不試試呢。其實擴展bootstrapTable的treegrid功能的思路博主已經有了,等有時間在下篇給出說明。
如果你覺得本文能夠幫助你,可以右邊隨意 打賞 博主,也可以 推薦 進行精神鼓勵。你的支持是博主繼續堅持的不懈動力。
本文原創出處:http://www.cnblogs.com/landeanfen/
歡迎各位轉載,但是未經作者本人同意,轉載文章之后必須在文章頁面明顯位置給出作者和原文連接,否則保留追究法律責任的權利
