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.