??????????最近在學習ExtJS,發現其服務器端是php,這一點對我們搞java的有點不厚道啊。昨天學習了ExtJS的樹,并做了一棵異步更新的樹出來,后臺的業務邏輯及持久層使用JPA+Spring2.5實現,發下面把步驟貼出來與大家分享。
首先準備樹的域模型,下面是一地區的域模型對象,
Region.java
的代碼如下:
@Entity

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

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

????
public
?Long?getId()?
{
????????
return
?id;
????}
????
public
?
void
?setId(Long?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>
<head>
<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8"?/>
<title>ExtJS-樹示例</title>
<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/ext-all.js"></script>
<script?type="text/javascript"?src="tree.js"></script>
</head>
<body>
<div??align="center">
??<p>ExtJS-樹的示例</p>?
</div>
<div?id="tree-div"></div>??
</body>
</html>

?
注意幾個<script>標簽,他們是用來引入ext的js以及本示例中用到的樹。tree.js的代碼如下:
Ext.BLANK_IMAGE_URL?=?'plugins/extjs/ext-2.0/resources/images/default/s.gif';

Ext.onReady(function()
{?

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

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

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

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

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

?
其實tree.js的代碼跟ExtJS官方示例中的差別不大,這里就不對這個代碼作詳細的解釋,如果你感興趣的話,可以在后面留言或給我發電子郵件。如果需要的人多的話,我看能否補充。這里把重點的代碼強調一下。

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

?
這個表示通過region.ejf?cmd=getRegion來加載樹的數據。另外,由于這里使用的是異步加載,所以還需要加下面一句:

tree.on('beforeload',function(node)
{????????
????????????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)

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

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

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

?
這個代碼說白了,就是根據客戶端的調用參數id值來加載該id下面的地區節點。RegionQuery是一個地區查詢類,主要是我不想寫sql或EJBQL,所以就用他了。大家主要看關鍵的部分,我們在調用service的getRegionBy方法返回的是一個分頁的地區Entity。所以要把這個地區Entity轉換成與Ext的樹節點數據匹配的方式,因此就有了下面一段代碼:
List<Node>?nodes=new?java.util.ArrayList<Node>();
????????for(int?i=0;i<pageList.getResult().size();i++)

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

?
下面我們看看Node這個類的實現,代碼如下:

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

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

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

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

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

????????
{
????????????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系列”中跟大家分享哪一方面的內容,請留言。