这几天用到了Extjs的TreePanel来创建树形菜单,在网上没瞧见一个好的示例,遂决定写一个,供后来者参考(本文下方的“附件下载”可下载本示例的完整源代码)。
先创建一个雏形,代码如下:
var tree = new Ext.tree.TreePanel({
title : '主菜单',
width : 200,
autoScroll : true,
singleExpand : true,
rootVisible : true,
animate : true,
//树加载器
loader : new Ext.tree.TreeLoader({
dataUrl : 'tree/treeNodeAction_listTree.action'
})
});
(代码1)
注意这里使用了一个loader属性,其值为一个Ext.tree.TreeLoader对象。根据它的名字,我们就能推测出这个对象是用来加载树的,而其dataUrl属性则指定应从何处加载树的结点信息。
紧接着创建根节点,并将此根结点设置为树的根:
var root = new Ext.tree.AsyncTreeNode({
id : 'root',
text : '根',
expanded : true
});
//将此根结点设置为树的根
tree.setRootNode(root);
(代码2)
这里注意以下几点:
1、AsyncTreeNode是一种异步加载的结点,即每次只加载本结点的直接子结点,而子结点的子结点(即当前结点的孙子结点)则在需要的时候再加载,这就提高了响应速度;
2、AsyncTreeNode也有loader属性,它的值继承自TreePanel中的loader属性(见代码1)。根结点使用此loader来加载它的子结点信息并创建子结点,而子节点也是AsyncTreeNode对象,它们的loader属性值也来自TreePanel的loader属性,它们也将使用此loader来加载自己的子结点信息,以此类推;
3、后台发送来的代表子结点信息的JSON字符串必须以方括号“[]”包含,如本例加载根节点的子结点信息的JSON字符串如下:
[{id:'module1',text:'模块1',leaf:false},{id:'module2',text:'模块2',leaf:true,url:'demo/module2.jsp'}]
(代码3)
加载效果如下图所示:
(图1:主菜单截图)
4、当加载结点的子结点时,会向后台传送一个node参数,值为当前结点的id。如加载根节点的子节点时,会把根节点的id值“root”(见代码2的根节点的id属性)传到后台,FireBug的截图如下:
(图2:node参数截图)
这样,后台就会从数据库中读取那些父结点的id为node值的结点信息,即当前结点的子结点信息。这样,本示例的结点信息在数据库表中的形式就如下图所示:
(图3:表中的结点信息)
注意观察parent_id值为root的两个结点,即为图1主菜单截图中的根结点的两个子结点。
同理,后台向前台发送结点信息时,也应该提供该结点的id值(参见代码3的JSON字符串),供将来加载此结点的子结点时使用。
到目前为止,我们已经成功生成了树,并可以动态加载其结点信息。但我们最终的目标应该是点击TreePanel(一般放置于页面的左边)的某个叶子结点时,能在页面右部显示相关的功能页面。为了实现这个功能,应该使用Viewport,其布局为border。然后将TreePanel放置于Viewport的西区,再新建一个TabPanel,放置于Viewport的中央区。为TreePanel添加click事件处理函数,在这个函数中,若相关的页面没有被加载,则新建一个Panel加载此页面,并添加到TabPanel中,然后设置此Panel为活动的,显示给用户;若相关页面已被加载,则直接设置其为活动的即可。具体代码如下所示(注意:其中的center即为位于中央区的TabPanel):
listeners : {
click : function(node) {
// node代表被点击的结点,通过其attributes属性读取
// 后台传来的url等属性,url指该结点指向的页面
var url = node.attributes.url;
var id = node.attributes.id;
// 从center中获取相关的Panel
var tab = center.getItem('tab_' + id);
// 只有当url存在,才进行后续处理
if (url) {
// 如果相关的Panel已存在,直接将其设置为活动的即可
if (tab) {
center.setActiveTab(tab);
} else {
// 否则创建一个Panel,用于加载url指向的页面,然后
// 将此Panel添加到center中,并设置其为活动的
tab = new Ext.Panel({
title : node.attributes.text,
html : "<iframe src='"
+ url
+ "'"
+ " width='100%' height='100%' frameborder='0' scrolling='auto'>"
+ "</ifame>",
closable : true,
layout : 'fit',
frame : false,
id : 'tab_' + id
});
center.add(tab);
center.setActiveTab(tab);
}
}
}
}
(代码4)
需要注意的是,只有叶子结点才有url属性,这一点从代码3的JSON字符串即可看出:id为module1的结点为非叶子结点,因而后台并没有给它传递url信息;而id为module2的结点为叶子结点,就有url属性。
效果如图所示(本示例的完整源代码请于本文下方的“附件下载”处下载):