Struts 2与AJAX(第二部分)

http://www.35java.com/zhibo/forum.php?mod=viewthread&tid=370&extra=page%3D3

更多<s:tree/>在Struts2的showcase中有两个<s:tree/>的例子,分别是静态树与动态树。所谓的静态树即是在编写JSP代码时通过<s:treenode/>生成树节点。我的上一篇文章的例子就是一个典型的静态树。而动态树则是在程序运行期间,Struts2运行时(Runtime)根据程序中的数据动态创建树节点。虽然在两个例子中<s:tree/>的theme属性都为“ajax”,但是从严格意义上来说,这两种树都不属于AJAX树,因为它们都是在输出页面时将全部节点加载到其中,而不是在父节点展开时通过XHR(XMLHttpRequest)获取节点数据。

动态树下面我们先看一下动态树的例子,接着再一步步地将其改造为名副其实的AJAX树。下例将会把WEB应用程序的目录树展现在JSP页面中。因此,我需要先包装一下java.io.File类,代码如下:

packagetutorial;

importjava.io.File;

public

classFileWrapper{

privateFilefile;

publicFileWrapper(Stringpath){

file=

newFile(path);

}

publicFileWrapper(Filefile){

this.file=file;

}

publicStringgetId(){

return

"file_"

+file.hashCode();

}

publicStringgetName(){

returnfile.getName();

}

publicStringgetAbsolutePath(){

returnfile.getAbsolutePath();

}

publicFileWrapper[]getChildren(){

File[]files=file.listFiles();

if(files!=

null

&&files.length>

0){

intlength=files.length;

FileWrapper[]wrappers=

newFileWrapper[length];

for(inti=

0;i<length;++i){

wrappers=

newFileWrapper(files);

}

returnwrappers;

}

return

newFileWrapper[0];

}

}

清单1src/tutorial/FileWrapper.java之所以需要对File类进行如此包装,是因为<s:tree/>用于动态树时,rootNode、nodeIdProperty、nodeTitleProperty和childCollectionProperty等属性都必填的。

然后是Action类的代码如下:

packagetutorial;

importjavax.servlet.http.HttpServletRequest;

importorg.apache.struts2.interceptor.ServletRequestAware;

importcom.opensymphony.xwork2.ActionSupport;

public

classDynamicTreeActionextendsActionSupportimplementsServletRequestAware{

private

static

final

longserialVersionUID=

1128593047269036737L;

privateHttpServletRequestrequest;

privateFileWrapperroot;

public

voidsetServletRequest(HttpServletRequestrequest){

this.request=request;

}

publicFileWrappergetRoot(){

returnroot;

}

@Override

publicStringexecute(){

root=

newFileWrapper(request.getSession().getServletContext().getRealPath("/"));

returnSUCCESS;

}

}

清单2src/tutorial/DynamicTreeAction.java上述代码取得WEB应用程序的根目录的绝对路径后,初始化FileWrapper对象root。该对象将为JSP页面的<s:tree/>的根节点。如下代码所示:

<%@pagelanguage="java"contentType="text/html;charset=utf-8"

pageEncoding="utf-8"%>

<%@taglibprefix="s"uri="/struts-tags"%>

<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Struts2AJAX-MoreTree</title>

<s:headtheme="ajax"debug="true"

/>

<scripttype="text/javascript">

/*<![CDATA[*/

functiontreeNodeSelected(arg){

alert(arg.source.title+'selected');

}

functiontreeNodeExpanded(arg){

alert(arg.source.title+'expanded');

}

functiontreeNodeCollapsed(arg){

alert(arg.source.title+'collapsed');

}

dojo.addOnLoad(function(){

vart=dojo.widget.byId('appFiles');

dojo.event.topic.subscribe(t.eventNames.expand,treeNodeExpanded);

dojo.event.topic.subscribe(t.eventNames.collapse,treeNodeCollapsed);

vars=t.selector;

dojo.event.connect(s,'select','treeNodeSelected');

});

/*]]>*/

</script>

</head>

<body>

<h2>

DynamicTreeExample

</h2>

<divstyle="float:left;margin-right:50px;">

<s:treeid="appFiles"theme="ajax"rootNode="root"

nodeTitleProperty="name"nodeIdProperty="id"

childCollectionProperty="children"

/>

</div>

</body>

</html>

清单3WebContent/Tree.jsp因为<s:tree/>的treeCollapsedTopic和treeExpandedTopic属性都没有起作用,所以如果我们想要监听这两个事件,就必须使用上述代码的方法。

最后是struts.xml配置文件:

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEstrutsPUBLIC

"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN"

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

<packagename="Struts2_AJAX_DEMO"extends="struts-default">

<actionname="DynamicTree"class="tutorial.DynamicTreeAction">

<result>Tree.jsp</result>

</action>

</package>

</struts>

清单4src/struts.xml发布运行应用程序,在浏览器地址栏中键入http://localhost:8080/Struts2_Ajax2/DynamicTree.action,有如下图所示页面:

图1动态树示例

AJAX树正如我在文章开头所说,Struts2所提供的静态树和动态树都不是严格意义上的AJAX树。下面就让我们来实现一个如假包换的AJAX树。首先要说明的是,Struts2的<s:tree/>默认是不支持这种按需加载数据的AJAX树。不过因为它是基于Dojo的树控件(Widget)所以要扩展也很方便。

Dojo通过名为“TreeRPCController”的控件实现AJAX树,它会监听被控制树的事件。当发生展开节点的事件时,TreeRPCController就会向URL发送XHR请求,该URL由TreeRPCController的RPCUrl属性定义。XHR请求格式类似如下格式:

http://localhost:8080/Struts2_Ajax2/AjaxTree.action?action=getChildren&data={"node":{"widgetId":"file_226092423","objectId":"C:\\ProgramFiles\\Tomcat5.5\\webapps\\Struts2_Ajax2","index":0,"isFolder":true},"tree":{"widgetId":"appFiles","objectId":""}}&dojo.preventCache=1182913465392

清单5XHR样本显而易见,请求中包含三个参数,分别是action为“getChildren”(固定值),data一个包含当前节点与树信息的JSON串和dojo.preventCache随机串,用于缓存不同节点的请求响应(父节点只会在第一次被展开时到服务器端加载数据,之后都是从浏览器的缓存中读取数据,可以提高应用程序性能)。

首先我要先写一个加载树节点数据的Action类,代码如下:

packagetutorial;

importjava.util.Map;

importcom.googlecode.jsonplugin.JSONExeption;

importcom.googlecode.jsonplugin.JSONUtil;

public

classAjaxTreeActionextendsDynamicTreeAction{

private

static

final

longserialVersionUID=

3970019751740942311L;

privateStringaction;

privateStringdata;

privateFileWrapper[]wrappers;

public

voidsetAction(Stringaction){

this.action=action;

}

public

voidsetData(Stringdata){

this.data=data;

}

publicFileWrapper[]getWrappers(){

returnwrappers;

}

@Override

publicStringexecute(){

if("getChildren".equals(action)){

try

{

Objecto=JSONUtil.deserialize(data);

Stringpath=((Map)((Map)o).get("node")).get("objectId").toString();

wrappers=

newFileWrapper(path).getChildren();

}

catch(JSONExeptione){

e.printStackTrace();

}

return

"ajax";

}

return

super.execute();

}

}

清单6src/tutorial/AjaxTreeAction.java上述代码可能需要解释一下:

相关推荐