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 相关文章推荐
快速排序 php与javascript的不同之处
Feb 22 Javascript
Jquery中&quot;$(document).ready(function(){ })&quot;函数的使用详解
Dec 30 Javascript
通过JS来动态的修改url,实现对url的增删查改
Sep 01 Javascript
JS实现控制表格行内容垂直对齐的方法
Mar 30 Javascript
JavaScript动态设置div的样式的方法
Dec 26 Javascript
模拟javascript中的sort排序(简单实例)
Aug 17 Javascript
request请求获取参数的实现方法(post和get两种方式)
Sep 27 Javascript
jQuery实现select模糊查询(反射机制)
Jan 14 Javascript
js实现按座位号抽奖
Apr 05 Javascript
详解VUE 对element-ui中的ElTableColumn扩展
Mar 28 Javascript
Angular6中使用Swiper的方法示例
Jul 09 Javascript
ajax跨域访问遇到的问题及解决方案
May 23 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
Godaddy空间Zend Optimizer升级方法
2010/05/10 PHP
ThinkPHP中使用ajax接收json数据的方法
2014/12/18 PHP
PHP中file_exists使用中遇到的问题小结
2016/04/05 PHP
PHP mysqli事务操作常用方法分析
2017/07/22 PHP
Javascript 实用小技巧
2010/04/07 Javascript
基于jquery &amp; json的省市区联动代码
2012/06/26 Javascript
jQuery查询数据返回object和字符串影响原因是什么
2013/08/09 Javascript
jquery 删除cookie失效的解决方法
2013/11/12 Javascript
jquery插件推荐 jquery.cookie
2014/11/09 Javascript
JavaScript中使用Object.create()创建对象介绍
2014/12/30 Javascript
JS实现淡蓝色简洁竖向Tab点击切换效果
2015/10/06 Javascript
jQuery 3.0十大新特性最终版发布
2016/07/14 Javascript
JavaScript每天必学之数组和对象部分
2016/09/17 Javascript
jQuery动态添加与删除tr行实例代码
2016/10/18 Javascript
微信小程序 循环及嵌套循环的使用总结
2017/09/26 Javascript
node.js部署之启动后台运行forever的方法
2018/05/23 Javascript
Vue 与 Vuex 的第一次接触遇到的坑
2018/08/16 Javascript
Vue.js中该如何自己维护路由跳转记录
2019/05/19 Javascript
在 Vue 中编写 SVG 图标组件的方法
2020/02/24 Javascript
[05:04]完美世界携手游戏风云打造 卡尔工作室地图界面篇
2013/04/23 DOTA
Python使用logging结合decorator模式实现优化日志输出的方法
2016/04/16 Python
Python面向对象class类属性及子类用法分析
2018/02/02 Python
Tensorflow实现AlexNet卷积神经网络及运算时间评测
2018/05/24 Python
如何在python中执行另一个py文件
2020/04/30 Python
Python sqlalchemy时间戳及密码管理实现代码详解
2020/08/01 Python
澳大利亚宠物食品和用品商店:PETstock
2020/01/02 全球购物
WSDL的操作类型主要有几种
2013/07/19 面试题
高三高考决心书
2014/03/11 职场文书
商业计算机应用专业自荐书
2014/06/09 职场文书
市场营销策划方案
2014/06/11 职场文书
学习雷锋月活动总结
2014/07/03 职场文书
人才市场接收函
2015/01/30 职场文书
培训通知书模板
2015/04/17 职场文书
通知函的格式
2015/04/27 职场文书
2019终止劳动合同协议书最新范本!
2019/07/09 职场文书
SQL优化老出错,那是你没弄明白MySQL解释计划用法
2021/11/27 MySQL