使
用JS生成树形结构的菜单是基于J2EE的B/S系统常用的UI方式。但长期以来的问题是同步(即一次加载整棵树)加载一棵完整的树给前台及后台同时带来
压力,由于加载数据及渲染时间过长使用户体验度很低。Ajax的异步数据传输方式是解决此问题的较好方式,即每次只加载一层节点,当需要时才加载下级节
点,这样页面无需一次加载解决了此问题。
Ext的TreePanel组件提供了此功能即异步树(asynchronism tree),使用其实现需以下两步:
- 后台数据加载的实现,并以JSON形式提供给前台。
- 前台Ext的Tree组件实现。
实现预览:
1. 首先是JavaBean的节点模型(Tree Node Model):
-
-
-
-
-
-
-
- public class AsyncTreeNode {
-
- private String id;
-
- private String text;
-
- private boolean leaf;
-
- private boolean disabled;
-
- private String cls;
-
- private String iconCls;
-
- private String href;
-
- private String hrefTarget;
-
- private boolean draggable;
-
-
- ... ...
- }
/**
* Method: 异步加载树型结点<br>
* Origin Time: 2008-8-15 下午03:56:28<br>
*
* @author Seraph<br>
* @email: seraph115@gmail.com<br>
*/
public class AsyncTreeNode {
private String id;
private String text; // 结点显示名称
private boolean leaf; // 是否为叶子结点
private boolean disabled; // 是否可用
private String cls; // 显示的样式,file、folder
private String iconCls; // 结点图标样式
private String href; // 点击后时的链接
private String hrefTarget; // 在何frame中显示
private boolean draggable; // 是否可拖拽
// Omit the get and set method
... ...
}
数据库中的表结构:
COLUMN_NAME
|
DATA_TYPE
|
ID |
NUMBER |
PARENT
|
NUMBER |
TEXT |
VARCHAR2 |
LEAF |
VARCHAR2 |
DISABLED |
VARCHAR2 |
CLS
|
VARCHAR2 |
ICON_CLS
|
VARCHAR2 |
HREF
|
VARCHAR2 |
HREF_TARGET
|
VARCHAR2
|
取下级节点的接口定义:
-
-
-
-
-
-
-
-
- public List getLowerTreeNodes(String menuId);
/**
* Method: 获取异步加载树型子结点<br>
* Author: Seraph<br>
* Origin Time: 2008-9-9 下午05:46:02<br>
*
* @param menuId 当前结点的id
* @return 下级节点组成的List
*/
public List getLowerTreeNodes(String menuId);
数据提供的Spring的Controller:
-
-
-
-
-
-
- public class AsyncTreeProviderController extends JsonProviderController {
-
- private TreeManager treeManager;
-
- public void setTreeManager(TreeManager treeManager) {
- this.treeManager = treeManager;
- }
-
- protected ModelAndView handleRequestInternal(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
-
- String rootId = request.getParameter("id");
- List list = treeManager.getLowerTreeNodes(rootId);
-
- super.pushJsonResponse(response, list);
-
- return null;
- }
-
- }
/**
* Method:<br>
* Origin Time: 2008-8-15 上午11:07:55<br>
* @author Seraph<br>
* @email: seraph115@gmail.com<br>
*/
public class AsyncTreeProviderController extends JsonProviderController {
private TreeManager treeManager;
public void setTreeManager(TreeManager treeManager) {
this.treeManager = treeManager;
}
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String rootId = request.getParameter("id");
List list = treeManager.getLowerTreeNodes(rootId);
super.pushJsonResponse(response, list);
return null;
}
}
此步将查询到的下级结点的List<AsyncTreeNode>转换为JSON数据通过Ajax方式返回给Ext的TreePanel组件用以渲染下级结点。
PS: 推荐使用json-lib来转换javabean为json数据。下面是json-lib的maven的dependency。
- <dependency>
- <groupId>net.sf.json-lib</groupId>
- <artifactId>json-lib</artifactId>
- <version>2.2.2</version>
- </dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.2</version>
</dependency>
2. JavaScript的Ext TreePanel组件实现:
-
-
-
-
-
-
-
- var AsyncTree = {
- author: "Seraph",
- version: "0.1.0"
- };
-
-
- var CG = {
- 1: "asyncTreeProvider.do",
- 2: "async"
- };
-
-
- var CN = {
- 1: "菜单",
- 2: "配置"
- };
-
-
- var ID = {
- 1: "-1",
- 2: "-2"
- };
-
- Ext.onReady(function(){
-
- var Tree = Ext.tree;
-
- var myTreeLoader = new Ext.tree.TreeLoader({
- url: CG[1]
- });
-
- myTreeLoader.on("beforeload", function(treeLoader, node) {
- myTreeLoader.baseParams.id = node.attributes.id;
- }, myTreeLoader);
-
- var tree = new Tree.TreePanel({
- el:'tree1',
- autoScroll:true,
- autoHeight: true,
- border: false,
- animate:true,
- enableDD:true,
- rootVisible:false,
- containerScroll: true,
- loader: myTreeLoader,
- root: {
- nodeType: CG[2],
- text: CN[1],
- id: ID[1]
- }
- });
- tree.render();
- tree.getRootNode().expand();
-
- });
/**
* async-tree.js Power by YUI-EXT and JSON.
*
* @author Seraph
* @email seraph115@gmail.com
*/
var AsyncTree = {
author: "Seraph",
version: "0.1.0"
};
// -> Configuration of tree. e.g: CG[1]
var CG = {
1: "asyncTreeProvider.do",
2: "async"
};
// -> Root-node name in Chinese. e.g: CN[1]
var CN = {
1: "菜单",
2: "配置"
};
// -> Root-node ID of tree. e.g: ID[1]
var ID = {
1: "-1",
2: "-2"
};
Ext.onReady(function(){
var Tree = Ext.tree;
var myTreeLoader = new Ext.tree.TreeLoader({
url: CG[1]
});
myTreeLoader.on("beforeload", function(treeLoader, node) {
myTreeLoader.baseParams.id = node.attributes.id;
}, myTreeLoader);
var tree = new Tree.TreePanel({
el:'tree1',
autoScroll:true,
autoHeight: true,
border: false,
animate:true,
enableDD:true,
rootVisible:false,
containerScroll: true,
loader: myTreeLoader,
root: {
nodeType: CG[2],
text: CN[1],
id: ID[1]
}
});
tree.render();
tree.getRootNode().expand();
});