这几天用到了Extjs的TreePanel来创建树形菜单,在网上没瞧见一个好的示例,遂决定写一个,供后来者参考(本文下方的“附件下载”可下载本示例的完整源代码)。

      先创建一个雏形,代码如下:


  1. var tree = new Ext.tree.TreePanel({  

  2.        title : '主菜单',  

  3.        width : 200,  

  4.        autoScroll : true,  

  5.        singleExpand : true,  

  6.        rootVisible : true,  

  7.        animate : true,  

  8.        //树加载器  

  9.        loader : new Ext.tree.TreeLoader({  

  10.           dataUrl : 'tree/treeNodeAction_listTree.action' 

  11.               })  

  12.     });  

(代码1)

      注意这里使用了一个loader属性,其值为一个Ext.tree.TreeLoader对象。根据它的名字,我们就能推测出这个对象是用来加载树的,而其dataUrl属性则指定应从何处加载树的结点信息。

      紧接着创建根节点,并将此根结点设置为树的根:


  1. var root = new Ext.tree.AsyncTreeNode({  

  2.               id : 'root',  

  3.               text : '根',  

  4.               expanded : true 

  5.            });  

  6. //将此根结点设置为树的根  

  7. 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):


  1. listeners : {  

  2.           click : function(node) {  

  3.              // node代表被点击的结点,通过其attributes属性读取  

  4.              // 后台传来的url等属性,url指该结点指向的页面  

  5.              var url = node.attributes.url;  

  6.              var id = node.attributes.id;  

  7.              // 从center中获取相关的Panel  

  8.              var tab = center.getItem('tab_' + id);  

  9.              // 只有当url存在,才进行后续处理  

  10.              if (url) {  

  11.                  // 如果相关的Panel已存在,直接将其设置为活动的即可  

  12.                  if (tab) {  

  13.                     center.setActiveTab(tab);  

  14.                  } else {  

  15.                     // 否则创建一个Panel,用于加载url指向的页面,然后  

  16.                     // 将此Panel添加到center中,并设置其为活动的  

  17.                     tab = new Ext.Panel({  

  18.                         title : node.attributes.text,  

  19.                         html : "<iframe src='" 

  20.                                + url  

  21.                                + "'" 

  22.                                + " width='100%' height='100%' frameborder='0' scrolling='auto'>" 

  23.                                + "</ifame>",  

  24.                         closable : true,  

  25.                         layout : 'fit',  

  26.                         frame : false,  

  27.                         id : 'tab_' + id  

  28.                     });  

  29.                     center.add(tab);  

  30.                     center.setActiveTab(tab);  

  31.                  }  

  32.              }  

  33.           }  

  34.       } 

(代码4)

      需要注意的是,只有叶子结点才有url属性,这一点从代码3的JSON字符串即可看出:id为module1的结点为非叶子结点,因而后台并没有给它传递url信息;而id为module2的结点为叶子结点,就有url属性。

      效果如图所示(本示例的完整源代码请于本文下方的&ldquo;附件下载&rdquo;处下载):

 


点击此处观看作者的MyBatis 3从入门到提高视频教程