zTree.js 官网API先容的灰常详细了,这里我们实战利用zTree.js构建一棵Tree树。
<!--more-->
写在前面 下列文章中讲述的实例,须要利用的后端数据是已经查询好的,这里我们不讲怎么查询数据,只讲如何利用现有的数据构建Tree树,详细的教程请查看我的 GitHub, 如果你以为写得好,欢迎star呀!
!
起步
利用zTree.js首先须要导入zTree的依赖库文件,传送门。
由于我利用了基于boostrap主题的zTree,以是还是建议大家去我的GitHub项目地址下载(CSS是修正过的),传送门:GitHub
页面中须要引入如下依赖库文件:
<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-->
前端构建一棵tree树查阅zTree.js官网API,构建一棵Tree树很大略:
一、前端初始化一个div,用来展示Tree树
zTree构建的Tree树是用iframe嵌套的,以是不用担心宽度、高度的问题
<div class=\"大众zTreeDemoBackground\公众> <ul id=\公众tree\公众 class=\"大众ztree\公众></ul></div>
初始化的div只须要关注id属性即可,由于JS中会根据这个ID找到构建Tree树的位置。
二、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才能渲染出一棵树。
Tree树数据构造剖析
首先,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-->
后端如何封装Tree树构造上面我们将的都是前端如何将JSON数据渲染成一棵Tree树,但是渲染用的数据该当是冲数据库中读取的。下面我们该当学习一下后端如何实现封装Tree树用的JSON数据。
SpringMVC我们利用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树的构造是一样的呢。没错,我们前端既然定义了这种格式,后端就必须要给它一个这样格式的数据。
Web层封装Tree数据构造
注: 本例中涉及的表设计请参考我的这篇博文 Shiro实现权限管理之表构造设计 。这里不再讲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实体类的数据。初始化一个空的List凑集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是什么?节点的id是每个节点的唯一标识,就像数据库的主键值一样,以是我们常日将其设置为数据库中的主键值。并且往后也要获取这个主键值。
节点名称是什么?节点的名称name是前端展示的各个节点的名称。而这些名称该当和数据库中的值是相同的,以是我们将其设置为数据库的description的值。
父节点是什么?父节点,我们在数据库中已经定义了,即数据库中存在一个字段pid,这个字段表示的是上级节点的id值,即如果存在上级节点(或叫上级分组),那么就给此row的pid字段设置为上级row的主键id值。
如何定义父节点?根据布局方法中的isParent字段,如果是父节点就直接手动设置为true,否则就设置为false。 如何判断是父节点?根据数据库(实体类)中已有的属性值pid判断,如果pid不为0就表示是子节点,如果pid是0便是父节点(由于主键值不可能为0)。