JQuery-zTree.js使用范例javascript
实现Tree树的插件不少,好比常见的UI:Layui、ElementUI、iView ... 。这里咱们介绍一个小巧的构建Tree树的插件 zTree.jsphp
zTree.js 官网API介绍的灰常详细了,这里咱们实战使用zTree.js
构建一棵Tree树。css
<!--more-->html
写在前面 下列文章中讲述的实例,须要使用的后端数据是已经查询好的,这里咱们不讲怎么查询数据,只讲如何使用现有的数据构建Tree树,详细的教程请查看个人 GitHub, 若是你以为写得好,欢迎star呀!!前端
使用zTree.js
首先须要导入zTree的依赖库文件,传送门。java
因为我使用了基于boostrap主题的zTree
,因此仍是建议你们去个人GitHub项目地址下载(CSS是修改过的),传送门:GitHubjquery
页面中须要引入以下依赖库文件:git
<link rel="stylesheet" href="static/lib/bootstrap.min.css"/> <link rel="stylesheet" href="static/lib/css/demo.css"/> <link rel="stylesheet" href="static/lib/css/metroStyle/metroStyle.css"/> <script type="text/javascript" src="static/lib/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="static/lib/jquery.ztree.core.min.js"></script> <script type="text/javascript" src="static/lib/jquery.ztree.excheck.min.js"></script>
<!--more-->github
查阅zTree.js官网API,构建一棵Tree树很简单:web
1、前端初始化一个div,用来展现Tree树
zTree构建的Tree树是用iframe嵌套的,因此不用担忧宽度、高度的问题
<div class="zTreeDemoBackground"> <ul id="tree" class="ztree"></ul> </div>
初始化的div只须要关注id
属性便可,由于JS中会根据这个ID找到构建Tree树的位置。
2、javaScript加载Tree树
为了真实点构建Tree树,我这里用一个json文件来模拟请求后端的数据。在同级目录下建立data.json
,在其中写入指定格式的JSON字符串:
[{ "id": 21, "name": "总经理", "pid": 0, "parent": true }, {"id": 26, "name": "技术部", "pid": 0, "parent": true }, { "id": 27, "name": "项目经理", "pid": 26, "parent": false }, {"id": 28, "name": "项目组组长", "pid": 26, "parent": false }, { "id": 29, "name": "安所有", "pid": 0, "parent": true }, {"id": 30, "name": "网络安所有负责人", "pid": 29, "parent": false }, { "id": 31, "name": "项目安全测试员", "pid": 29, "parent": false }]
而后,写JavaScript代码:
var setting = { view: { selectedMulti: true }, check: { enable: true, }, data: { simpleData: { enable: true,//是否采用简单数据模式 idKey: "id",//树节点ID名称 pIdKey: "pid",//父节点ID名称 rootPId: -1,//根节点ID } } }; $(function () { //加载后端构建的ZTree树(节点的数据格式已在后端格式化好了) $.ajax({ url: 'data.json', type: 'get', dataType: "json", success: (data) => { console.log(data); $.fn.zTree.init($("#tree"), setting, data);//初始化树节点时,添加同步获取的数据 }, error: (data) => { alert(data.message); } }); });
解释
setting
中包含了ztree
的全部配置。view
中包含了Tree树的一些视图样式配置,例如是否显示节点间的连线,是否显示节点的图标,等...selectedMulti
是view
的一个配置参数,设置是否容许同时选中多个节点。data
中包含了要展现的数据以及展现数据的配置,由于咱们采用了ajax请求数据,这里须要配置simpleData
。simpleData
数据展现的配置:enable
是否采用简单的数据模式;idKey
树节点ID名称;pIdKey
父节点ID名称;rootPId
根节点ID以上参数配置,你们最好去参看zTree.js官网API。
若是配置好了setting
,那下面就要ajax请求数据并渲染出来。如上在ajax的success回调函数中使用$.fn.zTree.init($("#treeID"),setting,data)
渲染树节点,其中第一个参数:树要渲染的位置、第二个参数:刚才写的setting
配置,第三个参数:要加载的数据。
如上,咱们先看下效果:
其中咱们最该关心的是如何实现节点的渲染,说白了就是要弄明白怎样的数据结构zTree
才能渲染出一棵树。
首先,zTree渲染节点须要的数据必定是JSON格式的数据,且JSON数据的格式和simpleData
配置参数有关;想要使用ajax这种方式渲染节点,你必须开启enable: true
,其次idKey
是树节点ID名称,也就是说树的每一个节点都有一个id,咱们在这里要指定被渲染的数据中展现id的名称;其次要指定pIdKey
,由于你的节点不会都是平级的没有子节点,当须要子节点,就必须指定一个区分父子节点的ID名称;最后就是rootPId
表示根节点ID,即最上层的节点ID,通常写为-1
便可。
此时,你或许应该参考一下我这篇文章:Shiro实现权限管理之表结构设计 ,表结构的设计和tree树的构建也算是有一部分的关系吧。
若是你的simpleData
是这样配置的:
simpleData: { enable: true,//是否采用简单数据模式 idKey: "id",//树节点ID名称 pIdKey: "pid",//父节点ID名称 rootPId: -1,//根节点ID }
那么你就应该提供这样的JSON数据:
[{"id": "xx", "pid": "xx", "pid": "xx"},{"id": "xx",....},{....}]
只要名称和JSON数据中对应就行,否则没法渲染出节点。
实现默认选中,就是在初始化树的时候,将(用户)已拥有的节点选项选中。要知道全部的节点数据应该是从数据库中读取出出来的,例如这篇博文 权限管理系统数据库表设计 中用户均可能拥有一个角色,那么在遍历角色树的时候就应该默认选中一些节点表述用户已经拥有了这个节点角色。
简单一句话:遍历须要默认选中的节点数据(ID..),调用zTree.js
相关的方法根据(ID)实现默认选中。
首先咱们须要了解:
函数 | 用处 |
---|---|
$.fn.zTree.getZTreeObj('') | 获取zTree对象,根据div中指定的ID获取此渲染的ZTree对象,下面的方法都用到此对象调用 |
zTree.selectNode(treeNode,addFlag,isSilent) | 根据上面获取的zTree对象调用selectNode ,参数一:要选中的节点数据;参数二:是否容许同时选中多个节点;参数三:为false选中节点自动滚动到可视区域,实现选中子节点的父节点默认展开 |
zTree.checkNode(treeNode,checked,checkTypeFlag,callbackFlag) | selectNode 只实现选择了节点,checkNode 实现勾选节点,参数二:是否勾选节点;参数三:勾选父节点是否联动勾选其下的子节点;参数四:是否自动触发beforeCheck & onCkeck 回调函数 |
zTree.getNodeByParam(key,value,parentNode) | 获取彻底匹配节点数据的JSON对象,参数一:要精确匹配的属性名称;参数二:要精确匹配的属性值;参数三:在某个父节点下查找 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="static/lib/bootstrap.min.css"> <link rel="stylesheet" href="static/lib/css/metroStyle/metroStyle.css"> <link rel="stylesheet" href="static/lib/css/demo.css"> </head> <body> <div class="zTreeDemoBackground"> <ul id="tree" class="ztree"></ul> </div> </body> <script type="text/javascript" src="static/lib/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="static/lib/jquery.ztree.core.min.js"></script> <script type="text/javascript" src="static/lib/jquery.ztree.excheck.min.js"></script> <script type="text/javascript"> var setting = { view: { selectedMulti: false }, check: { enable: true, }, data: { simpleData: { enable: true,//是否采用简单数据模式 idKey: "id",//树节点ID名称 pIdKey: "pid",//父节点ID名称 rootPId: -1,//根节点ID } } }; $(function () { //加载后端构建的ZTree树(节点的数据格式已在后端格式化好了) $.ajax({ url: 'data.json', type: 'get', dataType: "json", success: (data) => { console.log(data); $.fn.zTree.init($("#tree"), setting, data);//初始化树节点时,添加同步获取的数据 checkNodes(); }, error: (data) => { alert(data.message); } }); }); //处理默认选中的方法 function checkNodes(){ //模拟数据库中已存在的数据(要实现默认选中的数据) var data = [{"id": 21, "name": "总经理", "pid": 0},{"id":'27', "name": "项目经理", "pid": 26}]; var zTree = $.fn.zTree.getZTreeObj("tree"); //获取zTree对象 data.forEach(row => { zTree.selectNode(zTree.getNodeByParam("id", row.id), true, false); zTree.checkNode(zTree.getNodeByParam("id", row.id), true, false); }); } </script> </html>
如上,实现默认选中,在初始化树后当即调用处理默认选中的方法便可。咱们模拟默认选中的数据中包含了id和name以及pid,这些都是比较基础的数据,ztree的selectNode
和checkNode
方法都是根据id
实现选中的, 默认选中要提供的数据和渲染树用的格式是相同的。其中:
getZTreeObj()
将根据<div>中定义的id值来获取当前树的对象;selectNode
实现选择节点,不会勾选节点,可是它能实现将被勾选的子节点所在的父节点展开;checkNode
实现勾选节点,设置第三个参数是false,则表示选中父节点时不联动勾选其下的子节点,由于子节点未必都要默认选中。获取选中的节点,只须要了解zTree.getCheckedNodes()
,用来获取选中节点数据的JSON对象。其中获取到的选中节点数据包含必定顺序:选中父节点永远在选中子节点的最前面。
若是想要在提交表单的时候,将选中节点的值传给后台,就可使用getCheckedNodes()
方法获取到选中节点数据,而后遍历获得各个选中节点的数据。
实现单选,只须要在setting
的check
中配置chkStyle: "radio"
便可实现单选,可是,此时实现的单选只在同级节点上才能实现单选,也就是说你在同级节点上只能单选,可是在你能够同时选中子节点和父节点。
那么,在你调用getCheckedNodes()
方法获取选中节点数据时,其中也包含了选中的父节点,由于父节点可能只是个分组不必定要存入到数据库中;那么此时你就要判断下若是选中的节点的长度>0,那么就取索引位置的最后一个值;
<!--more-->
上面咱们将的都是前端如何将JSON数据渲染成一棵Tree树,可是渲染用的数据应该是冲数据库中读取的。下面咱们应该学习一下后端如何实现封装Tree树用的JSON数据。
咱们使用SpringMVC做为与后端交互的Web层框架,关于SpringMVC + Spring + Mybatis 框架的整合,你们能够参看个人这个项目 SSM框架整合
想必你们必定知道@ResponseBody
这个注解,若是方法或类上添加了这个注解,那么@RequestMapping()
映射return的东西将再也不是InternalResourcecViewResolver
视图解析器解析的视图地址,而是JSON格式的数据。 那么想要让SpringMVC相应一串[{"id": "xx", "name": "", "xx"},{"id": "xx", "name": "xx"}]
这种格式的数据,咱们就必须手动将数据封装成这种格式,如此SpringMVC才能将对象转换成JSON串。
咱们会想到,咱们能够将从数据库中读取的数据,依次存入到Map(或List)集合中,而后return map
。固然,这是可行的,可是或许麻烦了些,由于整个项目中不止要构建一棵Tree树,每次都要new Map重用率就过低了。因此,一个简单的方式,就是手动建立一个实体类TreeEntity.java
用以存放从数据库中读取到的数据,这样每次构建Tree树都能使用这个实体类对象。
TreeEntity.java
属性以下:
public class TreeEntity implements Serializable { private Long id; //节点的id值 private String name; //节点的名称 private Boolean isParent; //是不是父节点 private Long pid; //当前节点对应父节点的id值 public TreeEntity(Long id, String name, Boolean isParent, Long pid){ this.id = id; this.name = name; this.isParent = isParent; this.pid = pid; } getter/setter .... }
如上,你会发现,这是否是和咱们前面说的前端构建Tree树的结构是同样的呢。没错,咱们前端既然定义了这种格式,后端就必需要给它一个这样格式的数据。
这里再也不讲Dao层中如何查询的数据,咱们仅以一个最简单的查询(findAll
查询全部)来说述Tree的数据结构封装。
先看代码:
@ResponseBody @RequestMapping("/getZTreeForUserRoles") public List<TreeEntity> getTreeForUserRoles() { try { List<TreeEntity> treeList = new ArrayList<TreeEntity>(); List<Role> roleList = roleService.findAll(); for (Role role : roleList) { // 为tree树节点添加数据,节点pid为0的都是父节点,其余为子节点 if(role.getPid() != null){ if (role.getPid() == 0) { treeList.add(new TreeEntity(role.getId(), role.getDescription(), true, (long) 0)); } else { treeList.add(new TreeEntity(role.getId(), role.getDescription(), false, role.getPid())); } } } return treeList; } catch (Exception e) { e.printStackTrace(); return null; } }
解释
List<TreeEntity>
;即返回的是一个List集合,可是其中存的是TreeEntity实体类的数据。new ArrayList<TreeEntity>()
;并调用Service层的方法获取到sys_roles
表中的全部数据,固然findAll()
方法的返回值也是List集合。findAll()
查询到的数据;这就体现了返回值是List而且泛型是实体类的优点了,这样咱们能够直接经过实体类中定义的setter/getter
来存取数据。TreeEntity
中定义的带参构造方法,将3
中遍历获得的数据依次循环啊添加到List<TreeEntity>
集合中。List<TreeEntity>
集合返回。咱们来看一下这个请求映射返回的数据格式是如何的:
如上,咱们已经实现了目的。
拓展
上面的代码中还要说明的就是调用TreeEntity
的带参构造函数传入的参数值。咱们定义的带参构造函数以下:
public TreeEntity(Long id, String name, Boolean isParent, Long pid) { this.id = id; this.name = name; this.isParent = isParent; this.pid = pid; }
在为List<TreeEntity>
集合循环添加值时,要弄清楚:
节点的id是每一个节点的惟一标识,就像数据库的主键值同样,因此咱们一般将其设置为数据库中的主键值。而且之后也要获取这个主键值。
节点的名称
name
是前端展现的各个节点的名称。而这些名称应该和数据库中的值是相同的,因此咱们将其设置为数据库的description
的值。
父节点,咱们在数据库中已经定义了,即数据库中存在一个字段
pid
,这个字段表示的是上级节点的id
值,即若是存在上级节点(或叫上级分组),那么就给此row的pid
字段设置为上级row的主键id
值。
根据构造方法中的
isParent
字段,若是是父节点就直接手动设置为true,不然就设置为false。 如何判断是父节点?根据数据库(实体类)中已有的属性值pid
判断,若是pid不为0就表示是子节点,若是pid是0就是父节点(由于主键值不可能为0)。
<br/>
若是你们有兴趣,欢迎你们加入个人Java交流群:671017003 ,一块儿交流学习Java技术。博主目前一直在自学JAVA中,技术有限,若是能够,会尽力给你们提供一些帮助,或是一些学习方法,固然群里的大佬都会积极给新手答疑的。因此,别犹豫,快来加入咱们吧!
<br/>
If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.