JS 组件系列之 bootstrap treegrid 组件封装过程


Posted in Javascript onApril 28, 2017

正文

前言:最近产品需要设计一套相对完整的组织架构的解决方案,由于组织架构涉及到层级关系,在表格里面展示层级关系,自然就要用到所谓的treegrid。可惜的是,一些轻量级的表格组件本身并没有自带树形表格的功能,比如bootstrapTable就没有这个功能,怎么办呢?如果是jqgrid、easyUI的表格,treegrid的效果可以说是轻而易举就能解决,而项目目前用的就是bootstrapTable,不可能这个时候因为这个需求去换组件吧。博主分析了下,无非就两种解决方案:一种就是扩展bootstrapTable的treegrid功能;第二种就是再找一个单独的treegrid组件去实现这个功能。在网上找了下,找到了一个效果还不错的treegrid第三方组件,于是做了下封装,今天分享出来,供大家参考。

一、开源的treegrid

1、组件效果预览

最原始的效果

JS 组件系列之 bootstrap treegrid 组件封装过程

bootstrap样式的效果

JS 组件系列之 bootstrap treegrid 组件封装过程

JS 组件系列之 bootstrap treegrid 组件封装过程

这个是组件最原始的效果,后面会告诉大家博主做了哪些封装以及加了哪些功能。

在此还是给出一个封装过的效果吧!

JS 组件系列之 bootstrap treegrid 组件封装过程

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的文件。

JS 组件系列之 bootstrap treegrid 组件封装过程

然后就是最重要的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、代码的封装思路基本是参考博主之前介绍组件的封装 https://3water.com/article/112472.htm这一篇里面的内容来的。

3、defaults里面就是初始化组件的时候可以传递的参数,上述注释基本上写得比较清楚。id和parentId两个参数主要是用来描述数据之间的父子级关系,后面我们介绍组件时候的时候你一看就能明白。

4、博主加了几个自认为很有用的属性和方法,应该能减少一些使用的麻烦。比如初始化组件的时候是否展开所有的子节点、添加title、表格行的渐变色和表格边框等。

5、上述封装里面递归查找子节点的时候,每一次都需要遍历所有的数据去找子节点,效率偏低,如果你使用了类似linq to js之类的组件去操作js的集合,可以优化那部分代码,适当提高递归的效率。当然,如果你的结果集本身数据量不太大,这么写影响也不太大。

3、封装后的组件使用

我们在界面上面引用需要的css和js文件

<link href="~/Content/bootstrap/css/bootstrap.min.css" rel="external nofollow" rel="stylesheet" />
 <link href="~/Content/jquery-treegrid-master/css/jquery.treegrid.css" rel="external nofollow" 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得到的效果

JS 组件系列之 bootstrap treegrid 组件封装过程

增加隔行变色striped: true

JS 组件系列之 bootstrap treegrid 组件封装过程

增加表格边框bordered: true

JS 组件系列之 bootstrap treegrid 组件封装过程

综合效果

JS 组件系列之 bootstrap treegrid 组件封装过程

三、总结

至此本文就结束了,没有什么太高大上的技术,就是简单将一个第三方组件进行了一些封装,使得其使用起来更加方便而已。如果你项目中也正在为treegrid而纠结,何不试试呢。其实扩展bootstrapTable的treegrid功能的思路博主已经有了,等有时间在下篇给出说明。

以上所述是小编给大家介绍的JS 组件系统之 bootstrap treegrid 组件封装过程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Mootools 1.2教程 同时进行多个形变动画
Sep 15 Javascript
javascript算法题 求任意一个1-9位不重复的N位数在该组合中的大小排列序号
Jul 21 Javascript
Javascript获取HTML静态页面参数传递值示例
Aug 18 Javascript
jQuery实现多级下拉菜单jDropMenu的方法
Aug 28 Javascript
jQuery打字效果实现方法(附demo源码下载)
Dec 18 Javascript
JS排序方法(sort,bubble,select,insert)代码汇总
Jan 30 Javascript
模拟javascript中的sort排序(简单实例)
Aug 17 Javascript
Angular.js中控制器之间的传值详解
Apr 24 Javascript
详解vue服务端渲染(SSR)初探
Jun 19 Javascript
jQuery实现的上传图片本地预览效果简单示例
Mar 29 jQuery
解决微信浏览器缓存站点入口文件(IIS部署Vue项目)
Jun 17 Javascript
three.js 利用uv和ThreeBSP制作一个快递柜功能
Aug 18 Javascript
JavaScript实现简单的四则运算计算器完整实例
Apr 28 #Javascript
vue实现动态数据绑定
Apr 28 #Javascript
Vue实现动态响应数据变化
Apr 28 #Javascript
解决浏览器会自动填充密码的问题
Apr 28 #Javascript
JS实现简单的天数计算器完整实例
Apr 28 #Javascript
jQuery实现jQuery-form.js实现异步上传文件
Apr 28 #jQuery
js实现移动端编辑添加地址【模仿京东】
Apr 28 #Javascript
You might like
小谈php正则提取图片地址
2014/03/27 PHP
javascript 动态数据下的锚点错位问题解决方法
2008/12/24 Javascript
jquery ajax提交表单数据的两种实现方法
2010/04/29 Javascript
js控制滚动条缓慢滚动到顶部实现代码
2013/03/20 Javascript
javascript函数作用域学习示例(js作用域)
2014/01/13 Javascript
Jquery实现仿腾讯娱乐频道焦点图(幻灯片)特效
2015/03/06 Javascript
javascript关于运动的各种问题经典总结
2015/04/27 Javascript
jQuery实现图片渐入渐出切换展示效果
2015/08/15 Javascript
javascript中类的定义方式详解(四种方式)
2015/12/22 Javascript
关于网页中的无缝滚动的js代码
2016/06/09 Javascript
Vue.js每天必学之方法与事件处理器
2016/09/06 Javascript
使用jquery实现的循环连续可停顿滚动实例
2016/11/23 Javascript
详解HTML5 使用video标签实现选择摄像头功能
2017/10/25 Javascript
Vue.js在数组中插入重复数据的实现代码
2017/11/17 Javascript
element-ui上传一张图片后隐藏上传按钮功能
2019/05/22 Javascript
JavaScript提升机制Hoisting详解
2019/10/23 Javascript
微信小程序收藏功能的实现代码
2020/06/19 Javascript
Vue 中使用lodash对事件进行防抖和节流操作
2020/07/26 Javascript
如何在 ant 的table中实现图片的渲染操作
2020/10/28 Javascript
python函数的5种参数详解
2017/02/24 Python
Python+OpenCV图片局部区域像素值处理详解
2019/01/23 Python
python re的findall和finditer的区别详解
2020/11/15 Python
Python常用GUI框架原理解析汇总
2020/12/07 Python
爱尔兰领先的在线体育用品零售商:theGAAstore
2018/04/16 全球购物
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
国贸专业大学生职业生涯规划范文
2014/01/10 职场文书
军校大学生个人的自我评价
2014/02/17 职场文书
护士工作失误检讨书
2014/09/14 职场文书
2015年度护士个人工作总结
2015/04/09 职场文书
2015年学校安全工作总结
2015/04/22 职场文书
2015年护士节活动策划方案
2015/05/04 职场文书
本科毕业论文致谢怎么写
2015/05/14 职场文书
2015年班主任德育工作总结
2015/05/21 职场文书
写给医护人员的一封感谢信
2019/09/16 职场文书
Python使用OpenCV和K-Means聚类对毕业照进行图像分割
2021/06/11 Python
Java网络编程之UDP实现原理解析
2021/09/04 Java/Android