最近在学习ExtJS,发现其服务器端是php,这一点对我们搞java的有点不厚道啊。昨天学习了ExtJS的树,并做了一棵异步更新的树出来,后台的业务逻辑及持久层使用JPA+Spring2.5实现,发下面把步骤贴出来与大家分享。
		
				  
		
		
				首先准备树的域模型,下面是一地区的域模型对象,
				Region.java
				的代码如下:
		
		
				 @Entity
				@Entity

 public
				 
				class
				 Region
				public
				 
				class
				 Region 
				
						 {
				
				
						{
 @Id
    @Id
 @GeneratedValue(strategy 
						=
						 GenerationType.TABLE)
    @GeneratedValue(strategy 
						=
						 GenerationType.TABLE)
 private
						 Long id;
    
						private
						 Long id;

 @Field(name
						=
						"
						名称
						"
						,validator
						=
						@Validator(name
						=
						"
						string
						"
						,value
						=
						"
						min:2;max:50
						"
						,required
						=
						true
						))
    @Field(name
						=
						"
						名称
						"
						,validator
						=
						@Validator(name
						=
						"
						string
						"
						,value
						=
						"
						min:2;max:50
						"
						,required
						=
						true
						))
 @Column(length 
						=
						 
						100
						)
    @Column(length 
						=
						 
						100
						)
 private
						 String name;
    
						private
						 String name;
 
    
 @Field(name
						=
						"
						编码
						"
						,validator
						=
						@Validator(name
						=
						"
						string
						"
						,value
						=
						"
						min:2;max:16
						"
						,required
						=
						true
						))
    @Field(name
						=
						"
						编码
						"
						,validator
						=
						@Validator(name
						=
						"
						string
						"
						,value
						=
						"
						min:2;max:16
						"
						,required
						=
						true
						))
 @Column(length 
						=
						 
						100
						)
    @Column(length 
						=
						 
						100
						)
 private
						 String code;
    
						private
						 String code;
 @POLoad(name
						=
						"
						parentId
						"
						)
    @POLoad(name
						=
						"
						parentId
						"
						)
 @ManyToOne
    @ManyToOne
 private
						 Region parent;
    
						private
						 Region parent;
 @OneToMany(mappedBy 
						=
						 
						"
						parent
						"
						,fetch
						=
						javax.persistence.FetchType.EAGER)
    @OneToMany(mappedBy 
						=
						 
						"
						parent
						"
						,fetch
						=
						javax.persistence.FetchType.EAGER)
 private
						 List
						<
						Region
						>
						 children 
						=
						 
						new
						 java.util.ArrayList
						<
						Region
						>
						();
    
						private
						 List
						<
						Region
						>
						 children 
						=
						 
						new
						 java.util.ArrayList
						<
						Region
						>
						();

 public
						 Long getId()
    
						public
						 Long getId() 
						
								 {
						
						
								{
 return
								 id;
        
								return
								 id;
 }
    }
						
						
								
								 
								 public
						 
						void
						 setId(Long id)
    
						public
						 
						void
						 setId(Long id) 
						
								 {
						
						
								{
 this
								.id 
								=
								 id;
        
								this
								.id 
								=
								 id;
 }
    }
						
						
								
								 
						
				
		 
		  
Region域模型有parent,也有children,这个完全是一个树的结构,如果把树根砍了话就变成森林了,呵呵这一点跟现实不一样。其它都是setter及getter方法,这里就不多说了。
 
然后我们就要做针对这个地区信息的添删除改查了。呵呵,添删改查我最拿手了,特别是基于EJS(EasyJWeb+JPA+Spring2)构架的添删改查,一个命令搞定了。这里就不多说了,大家可以直接看我前段时间做的视频演示(http://www.easyjf.com/blog/html/20080102/1015814.html)。当然这个示例由于是分级别的,所以生成的添删改查还要改一改,才支持上下级管理功能。
 
下面进入我们重点部分,如何在页面中得到一棵表示地区的树。
首先准备一个tree.html,内容如下:
 <html>
<html>
 <head>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>ExtJS-树示例</title>
<title>ExtJS-树示例</title>
 <link rel="stylesheet" type="text/css" href="/plugins/extjs/ext-2.0/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="/plugins/extjs/ext-2.0/resources/css/ext-all.css" />
 <script type="text/javascript" src="plugins/extjs/ext-2.0/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="plugins/extjs/ext-2.0/adapter/ext/ext-base.js"></script>
 <script type="text/javascript" src="plugins/extjs/ext-2.0/ext-all.js"></script>
<script type="text/javascript" src="plugins/extjs/ext-2.0/ext-all.js"></script>
 <script type="text/javascript" src="tree.js"></script>
<script type="text/javascript" src="tree.js"></script>
 </head>
</head>
 <body>
<body>
 <div  align="center">
<div  align="center">
 <p>ExtJS-树的示例</p>
  <p>ExtJS-树的示例</p> 
 </div>
</div>
 <div id="tree-div"></div>
<div id="tree-div"></div>  
 </body>
</body>
 </html>
</html>

注意几个<script>标签,他们是用来引入ext的js以及本示例中用到的树。tree.js的代码如下:
 Ext.BLANK_IMAGE_URL = 'plugins/extjs/ext-2.0/resources/images/default/s.gif';
Ext.BLANK_IMAGE_URL = 'plugins/extjs/ext-2.0/resources/images/default/s.gif';

 Ext.onReady(function()
Ext.onReady(function() {
{ 

 var tree = new Ext.tree.TreePanel(
    var tree = new Ext.tree.TreePanel( {
{   
 el:"tree-div",
        el:"tree-div",   
 autoScroll:true,
        autoScroll:true,
 animate:true,
        animate:true,
 width:'100px',
        width:'100px',
 height:'300px',
        height:'300px',
 enableDD:true,
        enableDD:true,
 containerScroll: true,
        containerScroll: true, 

 loader: new Ext.tree.TreeLoader(
        loader: new Ext.tree.TreeLoader( {
{
 dataUrl:'region.ejf?cmd=getRegion'
            dataUrl:'region.ejf?cmd=getRegion'            
 })
        })
 });
    });

 tree.on("click",function(node,event)
    tree.on("click",function(node,event) {alert(node.id);});
{alert(node.id);});

 tree.on('beforeload',function(node)
    tree.on('beforeload',function(node) {
{        
 tree.loader.dataUrl = 'region.ejf?cmd=getRegion&id='+(node.id!='root'?node.id:"");
            tree.loader.dataUrl = 'region.ejf?cmd=getRegion&id='+(node.id!='root'?node.id:"");
 });
        });        

 var root = new Ext.tree.AsyncTreeNode(
    var root = new Ext.tree.AsyncTreeNode( {
{
 text: '地区信息',
        text: '地区信息',
 draggable:false,
        draggable:false,
 id:'root'
        id:'root'
 });
    });
 tree.setRootNode(root);
    tree.setRootNode(root);
 tree.render();
    tree.render();
 root.expand();
       root.expand();
 });
});

其实tree.js的代码跟ExtJS官方示例中的差别不大,这里就不对这个代码作详细的解释,如果你感兴趣的话,可以在后面留言或给我发电子邮件。如果需要的人多的话,我看能否补充。这里把重点的代码强调一下。

 loader: new Ext.tree.TreeLoader(
loader: new Ext.tree.TreeLoader( {
{
 dataUrl:'region.ejf?cmd=getRegion'
            dataUrl:'region.ejf?cmd=getRegion'            
 })
        })

这个表示通过region.ejf?cmd=getRegion来加载树的数据。另外,由于这里使用的是异步加载,所以还需要加下面一句:

 tree.on('beforeload',function(node)
tree.on('beforeload',function(node) {
{        
 tree.loader.dataUrl = 'region.ejf?cmd=getRegion&id='+(node.id!='root'?node.id:"");
            tree.loader.dataUrl = 'region.ejf?cmd=getRegion&id='+(node.id!='root'?node.id:"");
 });
        });  

那么region.ejf?cmd=getRegion是做什么的呢?就是从数据库中查询地区数据,并把他转换成JSon格式就OK了。下面是RegionAction中的getRegion方法的代码,如下所示:
 public Page doGetRegion(WebForm form)
public Page doGetRegion(WebForm form)

 
     {
{
 String id=CommUtil.null2String(form.get("id"));
        String id=CommUtil.null2String(form.get("id"));        
 RegionQuery query=new RegionQuery();
        RegionQuery query=new RegionQuery();
 query.setPageSize(-1);
        query.setPageSize(-1);        
 if(!"".equals(id))
        if(!"".equals(id))

 
         {
{
 Region parent=this.service.getRegion(new Long(id));
        Region parent=this.service.getRegion(new Long(id));
 query.setParent(parent);
        query.setParent(parent);        
 }
        }
 IPageList pageList=this.service.getRegionBy(query);
        IPageList pageList=this.service.getRegionBy(query);
 List<Node> nodes=new java.util.ArrayList<Node>();
        List<Node> nodes=new java.util.ArrayList<Node>();
 for(int i=0;i<pageList.getResult().size();i++)
        for(int i=0;i<pageList.getResult().size();i++)

 
         {
{
 Region region=(Region)pageList.getResult().get(i);
            Region region=(Region)pageList.getResult().get(i);
 nodes.add(new Node(region));
            nodes.add(new Node(region));
 }
        }    
 form.addResult("json",AjaxUtil.getJSON(nodes));
        form.addResult("json",AjaxUtil.getJSON(nodes));
 return Page.JSONPage;
        return Page.JSONPage;
 }
    }

这个代码说白了,就是根据客户端的调用参数id值来加载该id下面的地区节点。RegionQuery是一个地区查询类,主要是我不想写sql或EJBQL,所以就用他了。大家主要看关键的部分,我们在调用service的getRegionBy方法返回的是一个分页的地区Entity。所以要把这个地区Entity转换成与Ext的树节点数据匹配的方式,因此就有了下面一段代码:
 List<Node> nodes=new java.util.ArrayList<Node>();
List<Node> nodes=new java.util.ArrayList<Node>();
 for(int i=0;i<pageList.getResult().size();i++)
        for(int i=0;i<pageList.getResult().size();i++)

 
         {
{
 Region region=(Region)pageList.getResult().get(i);
            Region region=(Region)pageList.getResult().get(i);
 nodes.add(new Node(region));
            nodes.add(new Node(region));
 }
        }    

下面我们看看Node这个类的实现,代码如下:

 private class Node
private class Node  {
{
 private Region region;
        private Region region;
 Node(Region region)
        Node(Region region)

 
         {
{
 this.region=region;
            this.region=region;
 }
        }    

 public String getId()
        public String getId()  {
{        
 return region.getId().toString();
            return region.getId().toString();
 }
        }

 public boolean getLeaf()
        public boolean getLeaf()  {
{        
 return region.getChildren().size()<1;
            return region.getChildren().size()<1;
 }
        }        

 public String getText()
        public String getText()  {
{            
 return region.getName();
            return region.getName();
 }
        }    
 public String getQtip()
        public String getQtip()

 
         {
{
 return region.getName();
            return region.getName();
 }
        }
 }
    }

Node直接放在RegionAction中的,所以是Private的。这个Node所做的事就是把服务器的Region这个域模型适配成Ext的树状节点数据。在转换完以后,我们再看doGetRegion中的最后两句代码,如下所示:
    form.addResult("json",AjaxUtil.getJSON(nodes));
    return Page.JSONPage;
第一句代码是调用EasyJWeb中的AjaxUtil.getJSON方法直接把nodes这个List生成JSON数据;第二句告诉EasyJWeb这个模板使用的是JSONPage合成模板。呵呵,这个Page.JSONPage是这几天才加上去的,之前发布的m3没有,其实JSONPage模板的内容非常简单,内容如下:
function(){$!json}()
 
完成后,把这个Web应用打成war包,然后直接访问tree.html就能看到这个树了,大致如下图所示:
 
 
  
本示例已经被收录到了EasyJWeb的ajax综合示例中,里面还有更多的ajax示例,包括一个表格编辑的应用。
EasyJWeb的ajax综合示例的地址:http://easyjweb.demo.easyjf.com/ajax2/
本示例War包及源码:ftp://ftp1.easyjf.com/easyjweb/demo/ajax2.war (13M)
  本示例的源码:ftp://ftp1.easyjf.com/easyjweb/demo/ajax2-src.zip (665K)
 注:请下载后将db.properties里password改为你的密码。
你更希望我在“一起学ExtJS系列”中跟大家分享哪一方面的内容,请留言。