`
chenxueyong
  • 浏览: 336254 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

ExtJs中的树分析及实现

阅读更多

<网上抄录>

首先我们来看日志分类树,这一部分我将重点给大家分析ExtJS中的树,以及本示例中树的详细实现,包括如何与服务器进行数据交互,如果响应处理树及节点上的各个事件等.

树的显示:

首先,我们来看看日志管理菜单中包含异步树的代码,如下所示:

var categoryNode=new Ext.tree.AsyncTreeNode({

id:'root',

text:'日志内容管理',

loader.topicCategoryLoader

});

this.root.appendChild(categoryNode);

this.on('click',function(node.eventObject){

......//事件处理代码

});

日志内容管理这个节点下面就是日志分类子节点,这些节点都是异步加载的,所以需要给这个节点定义一个加载器,默认情况下,该节点的子节点也会使用父节点来加载其下面节点的数据.加载这些节点的加载器是topicCategoryLoader,topicCategoryLoader在manager.js中的定义,内容如下:

topicCategoryLoader:new Ext.tree.TreeLoader({

   url:"topicCategory.ejf?cmd=getCategory&pageSize=1&treeData=true",

listeners:{

      'beforeload':function(treeLoader,node){

        treeLoader.baseParams.id=(node.id!='root'?node.id:"");

   }

}

})

        url参数表示可以得到节点JSON数据的url地址.由于在TreeLoader中定义了beforeload事件响应函数,也就是在第一次要到服务器加载树节点信息之前,都会执行该响应函数,该事件响应函数可以带两个参数,第一个参数是指准备加载数据的TreeLoader对象,第二个参数是node表示要加载的节点.

       在响应函数中我们设置了加载器中baseParams对象的id属性值为所要加载节点的id或者空字符,根据当前节点的id值来判断.也就是说如果是加载id为root的节点,其会给服务器传递一个字符为空的id查询参数,类似于访问下面的url "/topicCategory.ejf?cmd=getCategory&pageSize=-1&treeData=true&id=";如果id为root以为的其他节点时,就是加载root节点的子节点的子节点(或孙子,重孙子节点时),都会把要加载的节点的id值作为参数传给服务器端,类似于直接访问url "/topicCategory.ejf?cmd=getCategory&pageSize=-1&treeData=true&id=xxx".

       服务器端返回节点为JSON数据格式,是一个包含各个节点对象的信息数组,如下所示:

[{

   id:1,

   text:"叶子节点1",

   leaf:true

},{

id:2,

   text:"叶子节点2, 包含子节点",

children:[{

   id: 3,
            text: '节点的子节点',
            leaf: true
        }]
}]

 由于每一个节点都是异步加载的,也就是只有在需要的时候才会到服务器加载这个子节点下的节点内容,因此不会一次性返回节点的children,异步加载的时候服务器返回的信息应该如下所示:
[{
        id: 1,
        text: '叶子节点1',
        leaf: true
    },{
        id: 2,
        text: '节点2,包含子节点',
        leaf: false
   }]

   注意,节点2由于还有子节点,所以其leaf属性值为false。这样Ext在构造这个节点的时候,会把该节点创建成一个节点加载节点,当我们点击该节点时又会再一次到服务器端加载数据。

       因此, topicCategory.ejf?cmd=getCategory&pageSize=-1&treeData=true这个url返回也就是一个JSON数据,包含了各个子节点的对象数组信息,在看这个url具体返回的内容之前,我们先看看topicCategory这段上模块后台代码.

后台数据加载

  topicCategory这个模块的Action为TopicCategoryAction,该类的命令处理方法只有一个,即用于提供树节点JSON数据的doGetCategory方法,代码如下:
@Action
public class TopicCategoryAction extends BaseAction {//继承BaseAction使得该模块的所有内容只有管理员才能访问
@Inject//表示自动注入这个service,由于前面使用@Action标签,所以这里其实可以省略
private ITopicCategoryService service;
//getCategory命令处理方法
public Page doGetCategory(WebForm. form)
{  
   String id=CommUtil.null2String(form.get("id"));//得到客户端不的查询参数id值
QueryObject query=form.toPo(QueryObject.class);//根据客户端的请求信息创建查询对象,由于请求的url中包含一个pageSize=-1的查询参数及值,所以这里会把调用queryObject.setPageSize方法把返回的数据设置为-1,也就是无限
if(!"".equals(id))//如果id不为空
   {
   TopicCategory parent=this.service.getTopicCategory(new Long(id));
   query.addQuery("obj.parent",parent,"=");//在查询对象中添加一个查询条件,表示查询parent为指定id的分类
   }
   else
   {
    query.addQuery("obj.parent is EMPTY",null);//否则,查询所有的根节点,这里使用的是标准的JPA对象查询语句
   }
   IPageList pageList=this.service.getTopicCategoryBy(query);//通过service的日志分类查询方法查询分类对象
   //然后是处理从业务层得到的查询数据,把他们转换成Ext树加载器需要的JSON格式
   String treeData = CommUtil.null2String(form.get("treeData"));
   if ("".equals(treeData)) {// 获得pageList的数组
   //这里省略这一部分,将在日志分类管理表格中用到
   }
   else{//这里表示为分类树提供信息
   List<Node> nodes=new java.util.ArrayList<Node>();//创建一个代表树节点的List
   if(pageList.getRowCount()>0){//如果数据库中查询到节点
   for(int i=0;i<pageList.getResult().size();i++)//循环每一个查询到的TopicCategory
   {
    TopicCategory category=(TopicCategory)pageList.getResult().get(i);
    nodes.add(new Node(category));//把TopicCategory转换成Node对象,并添加到返回列表中
   }
   }
   else//如果没有查询到任何节点,则创建一个“无分类”的节点返回给客户端
   {
   TopicCategory c=new TopicCategory();
   c.setName("无分类");
   c.setId(0l);
   nodes.add(new Node(c));
   }
   form.jsonResult(nodes);//把所有的树节点nodes作为json返回节点存放到form中
   }
   return Page.JSONPage;//返回JSON页面模板
}

  下面是代表客户端树节点信息一的Node类的定义,代码内容及详细注解如下:
private class Node {
   private TopicCategory category;
   Node(TopicCategory category)//接受一个TopicCategory为参数来构造节点
   {
    this.category=category;
   }
   public String getId() {   //获得节点的id
    return category.getId().toString();
   }
   public boolean getLeaf() {   //该节点是否是叶子节点
    return category.getChildren().size()<1;
   }  
   public String getText() {    //该节点的显示文本
    return category.getName();
   }
   public String getQtip()//该节点的快速提示信息
   {
    return category.getName();
   }
}
}

在浏览器输入地址:
topicCategory.ejf?cmd=getCategory&pageSize=-1&treeData=true

你会得到类似下面的内容:
function(){var v0=[];var v1={};var v6={};var v11={};var v16={};var v21={};var v26={};var v31={};var v36={};var v41={};var v46={};var v51={};var v56={};v1.id="1".replace(/"/g, '"');v1.leaf=false;v1.qtip="JAVA".replace(/"/g, '"');v1.text="JAVA".replace(/"/g, '"');v6.id="2".replace(/"/g, '"');v6.leaf=true;v6.qtip="Dot\ Net".replace(/"/g, '"');v6.text="Dot\ Net".replace(/"/g, '"');v11.id="3".replace(/"/g, '"');v11.leaf=true;v11.qtip="PHP".replace(/"/g, '"');v11.text="PHP".replace(/"/g, '"');v16.id="4".replace(/"/g, '"');v16.leaf=true;v16.qtip="ASP".replace(/"/g, '"');v16.text="ASP".replace(/"/g, '"');v21.id="5".replace(/"/g, '"');v21.leaf=true;v21.qtip="ROR".replace(/"/g, '"');v21.text="ROR".replace(/"/g, '"');v26.id="6".replace(/"/g, '"');v26.leaf=true;v26.qtip="Python".replace(/"/g, '"');v26.text="Python".replace(/"/g, '"');v31.id="7".replace(/"/g, '"');v31.leaf=true;v31.qtip="ExtJS\u6587\u7AE0".replace(/"/g, '"');v31.text="ExtJS\u6587\u7AE0".replace(/"/g, '"');v36.id="65536".replace(/"/g, '"');v36.leaf=false;v36.qtip="Other".replace(/"/g, '"');v36.text="Other".replace(/"/g, '"');v41.id="65543".replace(/"/g, '"');v41.leaf=false;v41.qtip="\u5FC3\u60C5".replace(/"/g, '"');v41.text="\u5FC3\u60C5".replace(/"/g, '"');v46.id="65544".replace(/"/g, '"');v46.leaf=true;v46.qtip="\u5FC3\u60C5".replace(/"/g, '"');v46.text="\u5FC3\u60C5".replace(/"/g, '"');v51.id="65546".replace(/"/g, '"');v51.leaf=true;v51.qtip="\u7F51\u7EDC\u65E5\u5FD7".replace(/"/g, '"');v51.text="\u7F51\u7EDC\u65E5\u5FD7".replace(/"/g, '"');v56.id="65547".replace(/"/g, '"');v56.leaf=true;v56.qtip="\u554A\u554A".replace(/"/g, '"');v56.text="\u554A\u554A".replace(/"/g, '"');v0[0]=v1;v0[1]=v6;v0[2]=v11;v0[3]=v16;v0[4]=v21;v0[5]=v26;v0[6]=v31;v0[7]=v36;v0[8]=v41;v0[9]=v46;v0[10]=v51;v0[11]=v56;return v0;}()

 

  你不用担心你看不懂,这是一个经过压缩及转码后json数据,他是直接把服务器端的对象自动转换成的,由于我们返回的json数据nodes类型为list,所以返回给端的时候会变成一个数组,数组中的每一元素都是Node对象,包含getter方法中定义的属性及值,如leaf、text、id等。

捕捉事件

  在日志分类树的菜单中,我们点击内容管理中某一个子节点的时候,都需要在主工作区中打开该分类的日志内容管理面板,要实现该功能,就需要定义节点的事件响应函数,这里是click事件响应,其代码及详细注解如下所示:
on方法是addListener方法的缩写,用于给组件添加事件响应函数(也可以理解为事件监听器或事件处理器)
this.on("click",function(node,eventObject)//树的click事件响应函数可以包含两个参数,第一个表示当前点击的节点node,第二个表示Ext的事件对象
   {
   if(node==categoryNode||categoryNode.contains(node)){//首先判断点击的节点是不是属于内容管理节点,或者是内容管理节点下面的子节点,如果是则执行下面的操作。如果点击的是其它节点,比如菜单中的“写日志”则不用管。
      var panel=Ext.getCmp("topicListPanel");//得到日志内容管理面板
      if(!panel){
        panel=new TopicListManage();//如果没有,则直接用new创建一个
        removeTopic=function(id){//给全局的函数removeTopic赋初值
         panel.grid.getSelectionModel().selectRecords([panel.store.getById(id)]);//选中表格中指定id的记录
         panel.removeData();//调用面板的removeData方法删除数据
};
        editTopic=function(id){//给全局的editTopic函数赋值
         panel.grid.getSelectionModel().selectRecords([panel.store.getById(id)]);//在表格中选中指定id的行
         panel.edit();//调用表格的edit方法来编辑选中的行
         };
       }
      main.openTab(panel);//在主工作区中打开内容管理面板,并设置成当前活动面板
   panel.store.baseParams.categoryId=(node.id!='root'?node.id:"");//根据当前点击的节点指定表格中数据加载时传递的categoryId值
      panel.store.removeAll();//首先删除表格中所有数据
      panel.store.reload();//然后重新加载表格中的数据
   }

另外一颗树

  日志分类树除了菜单面板中这一棵以外,当点击分类管理的时候,日志分类管理程序中也会出现另外一棵树,如下图所示:

  日志分类管理里面的这一树树比较简单,就是直接是一树普通的树而已,其仍然是使用topicCategoryLoader作为树节点数据加载器。定义这棵树的代码及注解如下:
//直接调用new方法来初始化一个Ext.tree.TreePanel面板,用来显示树结构的信息
this.tree=new Ext.tree.TreePanel({title:"日志分类",//树面板的标题
     region:"west",//面板所在区域
     width:150,//面板的宽度
     root:new Ext.tree.AsyncTreeNode({//树根为一棵异步加载树
      id:"root",//树跟的id为特殊值root
        text:"日志分类", //定义树根显示的文本
        expanded:true,//可以展开
        loader:topicCategoryLoader//指定根节点的加载器
    });

这棵树的事件响应函数也比较简单,就是根据节点的信息直接重新加载表格中的数据。其代码及注解如下:
//调用组件的on方法来添加事件响应函数,参数click表示添加针对click事件的响应函数
this.tree.on("click",function(node,eventObject){//响应函数包含两个参数,第一个表示当前点击的节点
var id=(node.id!='root'?node.id:"");//得到日志分类对象的真实id
this.store.baseParams.id=id;//设置表格中的加载子节点时的id
this.store.removeAll();//删除表格中的所有数据
this.store.load();//重新加载表格中的数据
},this); //最后一个参数this表示事件响应函数的作用域为this

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics