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 相关文章推荐
(function(){})()的用法与优点
Mar 11 Javascript
为调试JavaScript添加输出窗口的代码
Feb 07 Javascript
jQuery asp.net 用json格式返回自定义对象
Apr 07 Javascript
js null undefined 空区别说明
Jun 13 Javascript
js判断一个元素是否为另一个元素的子元素的代码
Mar 21 Javascript
jQuery 在光标定位的地方插入文字的插件
May 10 Javascript
js控制input输入字符解析
Dec 27 Javascript
利用JS解决ie6不支持max-width,max-height问题的方法
Jan 02 Javascript
JS简单判断函数是否存在的方法
Feb 13 Javascript
如何使用Bootstrap 按钮实例详解
Mar 29 Javascript
利用SpringMVC过滤器解决vue跨域请求的问题
Feb 10 Javascript
如何实现一个webpack模块解析器
Oct 24 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增删改查示例自己写的demo
2013/09/04 PHP
用js实现的检测浏览器和系统的函数
2009/04/09 Javascript
获得Javascript对象属性个数的示例代码
2013/11/21 Javascript
js 限制input只能输入数字、字母和汉字等等
2013/12/18 Javascript
jQuery实现返回顶部功能适合不支持js的浏览器
2014/08/19 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
2014/09/03 Javascript
Backbone.js中的集合详解
2015/01/14 Javascript
JavaScript通过setTimeout实时显示当前时间的方法
2015/04/16 Javascript
Bootstrap每天必学之面板
2015/11/30 Javascript
js仿百度切换皮肤功能(html+css)
2016/07/10 Javascript
旺旺在线客服代码 旺旺客服代码生成器
2018/01/09 Javascript
JavaScript实现计算多边形质心的方法示例
2018/01/31 Javascript
Vue的轮播图组件实现方法
2018/03/03 Javascript
vue中使用iview自定义验证关键词输入框问题及解决方法
2018/03/26 Javascript
基于vue+axios+lrz.js微信端图片压缩上传方法
2019/06/25 Javascript
vue中使用v-model完成组件间的通信
2019/08/22 Javascript
[02:31]DOTA2帕克 英雄基础教程
2013/11/26 DOTA
[06:07]DOTA2-DPC中国联赛 正赛 Ehome vs VG 选手采访
2021/03/11 DOTA
尝试使用Python多线程抓取代理服务器IP地址的示例
2015/11/09 Python
Python单例模式实例详解
2017/03/01 Python
Python判断字符串是否为字母或者数字(浮点数)的多种方法
2018/08/03 Python
详解python tkinter教程-事件绑定
2019/03/28 Python
详解Django3中直接添加Websockets方式
2020/02/12 Python
使用opencv中匹配点对的坐标提取方式
2020/06/04 Python
python实现简单的学生管理系统
2021/02/22 Python
人事行政主管岗位职责
2013/12/22 职场文书
实用求职信范文分享
2013/12/25 职场文书
光盘行动倡议书
2014/02/02 职场文书
小班重阳节活动方案
2014/02/08 职场文书
三八节标语
2014/06/27 职场文书
中职招生先进个人材料
2014/08/31 职场文书
项目委托协议书(最新)
2014/09/13 职场文书
房屋授权委托书范本
2014/10/07 职场文书
幼儿园见习报告
2014/10/30 职场文书
上个世纪50年代的可穿戴技术:无线电帽子
2022/02/18 无线电
python神经网络学习 使用Keras进行简单分类
2022/05/04 Python