简介树控件
树控件,很常用,比如它可以做有层级的菜单。比如公司的部分划分、省市区的选择。。。
最大的好处就是有层级关系,看起来和选择起来都比较清晰,就像一串杂乱无章的json你用bejson去格式化一样的感觉。
所以,掌握树控件,是很有必要的,为什么?因为它很有用,用的恰当,也可以适当提升用户体验,
树的两种加载方式
tree的加载方式分为常规加载和异步加载两种,其中常规加载也就是一次性把整棵树都加载出来,而异步加载呢就是当展开父节点的时候才会去加载子节点。
一般肉眼上可以观察到异步加载的树在点击父节点的时候会有一个load...的提示~其次呢你也可以抓包看~
好的~今天先来说说同步加载,这个在数据量较小的时候比较常用,还有一种情况,也不得不用同步加载,那就是在需要全选或者级联选择的时候。若使用异步加载,那么当你若未展开父节点而勾选了父节点的话,实际上只有父节点被选中(因为此时其下的子节点还未加载),也就造成了“选择丢失”,所以这种情况需要使用同步加载。如果数据量很大,那么需要给出适当的进度提示来提升用户体验。
Tree In EasyUI
看看tree在easyUI中的具体用法吧:
两种方式来加载数据:一种是通过js的data,另外一种通过li标签
<ul class="easyui-tree" data-options="url:'tree_data1.json',method:'get',animate:true"></ul>
<ul class="easyui-tree"> <li> <span>My Documents</span> <ul> <li data-options="state:'closed'"> <span>Photos</span> <ul> <li> <span>Friend</span> </li> <li> <span>Wife</span> </li> </ul> </li> <li> <span>Program Files</span> <ul> <li>Java</li> <li>Games</li> </ul> </li> <li>welcome.html</li> </ul> </li> </ul>
简单吧,个人推荐使用js的方式来加载比较复杂的内容,因为组装数据比较容易一些,弄个json丢过来就好了~
当然若使用Velocity等模板引擎,第二种方式似乎也不错吧~好,言归正传,通过上面那一行代码相信你也明白了,tree的关键在于如何组织数据结构。
首先,我们要返回一个数组给前台,结构类似:
[ { "id": 1, "text": "My Documents", "children": [ { "id": 11, "text": "Photos", "state": "closed", "children": [ { "id": 111, "text": "Friend" }, { "id": 112, "text": "Wife" } ] }, { "id": 14, "text": "about.html" }, { "id": 15, "text": "welcome.html" } ] } ]
这里简单解释一下,这个数组中的每一个json代表的是最高层的父级,如上只定义了一个最高父级——My Documents。并且这个最高父级下面包含了3个子级,分别是id为11的Photos、id为14的about、和id为15的welcome,相信应该看的出来吧。
id一般为该节点的标识符,唯一
text则为该节点显示的文本
state指定该节点的是展开的还是关闭的(对叶子节点无效)‘open‘ or ‘closed‘.
其中Photos还有子级。。。而about和welcome则不包含子级,这种特殊的节点叫做叶子节点。相信大家也观察到了,children这个属性决定了该节点是否是叶子节点,若该属性不存在或为空,那么它就是叶子节点。
可以看看上述json对应的树:
这里也可以看到easyUI默认对于叶子节点的显示是以“文档图标”,而对于包含下级的节点用的是“文件夹图标”,替换这些图标也很简单。
只需要在上述的属性中,再定义一个iconCls就可以了~
像这样的树一般是单选树,若我们想要支持多选的话,可以为每个图标前加上一个多选框checkbox
<ul class="easyui-tree" data-options="checkbox:true,url:'tree_data1.json',method:'get',animate:true"></ul>
加上这个的话,那么树节点又多了一种属性——checked,也就是指明是否被选中了。
所以总结一下,我们可以为tree node定义的属性有这么五种:id、text、state、iconCls、checked,别忘了,还有一个children,这个children也是一个treenode
那么在后台我们可以定义一个TreeNode的实体,方便我们组装数据和整理逻辑。
具体应用
大概知道了tree的用法,那么我们来考虑这么一个场景,在酒店中,不同的服务员需要服务不同的客房,比如说小王负责4层和5层,小李负责6层和7层,当然也可能小王只负责三楼的部分房间。另外,酒店里可能会有不同的楼,比如主楼和副楼,客房是归在这些楼里的。考虑到这里是要选择具体房间的,所以使用同步加载比较合理。
在这里可以归纳出几个层级关系:楼 号——》层号——》房间号(从高到低),并且楼号和层号都为父节点,而房间为叶子节点
并且我们这里考虑加一个 “所有房间” 父节点,方便全选:
List<RoomNode> treeNodeList = new ArrayList<RoomNode>(1); // 第一层仅有一个head RoomNode head = new RoomNode(); List<Node> children = new ArrayList<Node>(); head.setChildren(children); head.setId("0"); head.setText("所有房间"); head.setIconCls("icon-tree-room-all"); treeNodeList.add(head); List<Hall> buildingList = roomService.getAllHalls(); for (Hall _building : buildingList) { List<GuestRoom> guestRoomList = roomService .getAllGuestRoomsFloor(_building.getBg_code()); List<Node> floorList = new ArrayList<Node>(guestRoomList.size()); RoomNode building = new RoomNode(); building.setId(_building.getBg_code()); building.setText(_building.getBg_descript()); building.setState("closed"); building.setChildren(floorList); building.setIconCls("icon-tree-room-building"); children.add(building); for (GuestRoom _floor : guestRoomList) { guestRoomList = roomService.getGuestRooms(_floor.getFloor(), _building.getBg_code()); List<Node> roomList = new ArrayList<Node>(guestRoomList.size()); RoomNode floor = new RoomNode(); floor.setId(_floor.getFloor()); floor.setText(_floor.getFloor() + "楼"); floor.setState("closed"); floor.setChildren(roomList); floor.setIconCls("icon-tree-room-floor"); floorList.add(floor); for (GuestRoom _room : guestRoomList) { RoomNode room = new RoomNode(); if (checkedRooms != null && checkedRooms.indexOf(_room.getRoomno().trim()) > -1) { room.setChecked(true); } room.setId(_room.getRoomno().trim()); room.setText(_room.getRoomno() + "房"); room.setIconCls("icon-tree-room-room"); roomList.add(room); } } }
同步加载的整个过程还是比较简单,无非也就是通过setChildren来搞定父子级的关系,没有别的复杂操作了。
下面给出一张替换完图标的效果图:
在下一篇文章中,将会介绍异步树的使用方法~这种树的优点就是用到的时候再加载,节省了不必要的流量以及加载时间。