Struts 2.0系列教程(十四)Struts2与AJAX(第二部分),最新最酷的S truts2.0 Ajax教程案例,详细讲述了如何在Struts下正确配置Ajax应用.欢
迎在线阅读Struts2 Ajax演示教程.
本文深入讲解
更多
在Struts2的s howcas e中有两个
动态树。所谓的静态树即是在编写JSP代码时通过
两个例子中
这两种树都不属于AJAX树,因为它们都是在输出页面时将全部节点加载到其中,而不是在父节点展开时通过XHR(XMLHttpRequest)获取节点数据。
动态树
下面我们先看一下动态树的例子,接着再一步步地将其改造为名副其实的A JAX 树。下例将会把W EB应用程序的目录树展现在JSP页面中。因此,我需
要先包装一下java.io.File 类,代码如下:
pac kage t ut orial;
impo rt java.io.File;
pub lic c lass FileWrap per {
privat e File file;
public FileW rap per(St rin g pat h) {
file = new File(pat h);
}
public FileW rap per(F ile file) {
t his.file = f ile;
}
public St rin g get Id() {
ret urn"file_" + file.has hCode();
}
public St rin g get Na me() {
ret urn file.get Na me();
}
public St rin g get Absolut ePat h() {
ret urn file.get Absolut ePat h();
}
public FileW rap per[] get Ch ild ren() {
File[] files = file.list Files();
if(files != nu ll&& files.len gt h > 0) {
int lengt h = files.le ngt h;
FileWra ppe r[] wrap pers = new File Wra ppe r[le ngt h];
for(int i = 0; i < lengt h; ++i) {
wrappers[i] = new FileW rap per(f iles[i]);
}
ret urn wra ppe rs;
}
ret urn new FileW rappe r[0];
}
}
清单 1 s rc/tutori al/F il eWrapper.j ava
之所以需要对File类进行如此包装,是因为
然后是Action类的代码如下:
pac kage t ut orial;
impo rt javax.se rvlet.ht t p.Ht t pServlet Req uest;
impo rt org.apac he.st rut s2.int erc ept or.Servlet Req uest Aware;
impo rt c om.opensy mp hony.xwo rk2.Ac t ionSup po rt;
pub lic c lass Dyna mic T reeAc t ion ext ends Act ionSup port imp le ment s Servlet Re quest Aware {
privat e st at ic final lo ng seria lVers ion UID = 11285930472690367 37L;
privat e Ht t pServlet Req uest request;
privat e FileWra ppe r root;
public void set Servlet Request(Ht t pServlet Req uest request) {
t his.request= re quest;
}
public FileW rap per get Root() {
ret urn root;
}
@Override
public St rin g exec ut e() {
root= new FileW rap per(req uest.get Session().get Servlet Cont e xt().get Rea lPat h("/"));
ret urn SUCC ESS;
}
}
清单 2 s rc/tutori al/D ynam i cTreeActi on.j ava
上述代码取得W EB应用程序的根目录的绝对路径后,初始化FileW rapper 对象root。该对象将为JSP页面的
<%@ page la ngua ge="java" c ont ent T ype="t ext/ht ml; c harset=ut f -8"
pageEnc oding="ut f-8"%>
<%@ t ag lib prefix="s" uri="/st rut s-t ags"%>
/*
funct ion t reeNodeSe lec t ed(arg) {
alert(arg.sou rc e.t it le + ' selec t ed');
}
funct ion t reeNodeE xpande d(arg) {
alert(arg.sou rc e.t it le + ' expan ded');
}
funct ion t reeNodeCo llapsed(a rg) {
alert(arg.sou rc e.t it le + ' c ollapse d');
}
dojo.addO nLoa d(func t ion() {
var t= do jo.widget.byId('appF iles');
dojo.event.t opic.subsc ribe(t.event Na mes.e xpa nd, t reeN odeE xpan ded);
dojo.event.t opic.subsc ribe(t.event Na mes.c olla pse, t reeN odeCollapse d);
var s = t.select or;
dojo.event.c onnect(s, 'select', 't reeNodeSe lec t ed');
});
/*]]> */
Dyna mic T ree Exa mp le
nodeT it lePropert y="na me"node IdP rope rt y="id" c hildCo llec t ionPrope rt y="c hil d ren"/>
清单 3 WebContent/Tree.j s p
因为
最后是s truts.x ml配置文件:
"-//Apac he Soft ware Foundat ion//DT D St rut s Configu rat ion 2.0//E N"
"ht t p://st rut s.apac https://www.doczj.com/doc/1517314304.html,/dt ds/st rut s-2.0.dt d">
清单 4 s rc/s truts.xm l
发布运行应用程序,在浏览器地址栏中键入http://localhos t:8080/Stru ts2_Ajax2/DynamicTree.act ion,有如下图所示页面:
图1 动态树示例
AJAX 树
正如我在文章开头所说,Struts2所提供的静态树和动态树都不是严格意义上的AJAX树。下面就让我们来实现一个如假包换的AJAX树。首先要说明的是,Struts2的
Dojo 通过名为“TreeRPCController”的控件实现AJAX 树,它会监听被控制树的事件。当发生展开节点的事件时,TreeRPCController就会向URL发送XHR请求,该URL由TreeRPCController的RPCUr l 属性定义。XHR请求格式类似如下格式:
ht t p://loc alhost:8080/St rut s2_A ja x2/Aja xT ree.ac t ion?ac t ion=get Ch ildre n&dat a={"node":{"w idget Id":"file_226092423","ob jec t Id":"C:\\Pro gra m Files\\T o mc at 5.5\\webapps\\St rut s2_Aja x2","inde x":0,"isFolde r": t rue},"t ree":{"w id get Id":"ap pFiles","ob jec t Id":""}}&do jo.p revent Cac he =1182913465392
清单 5 XH R样本
显而易见,请求中包含三个参数,分别是action为“getChildren”(固定值),data一个包含当前节点与树信息的JSON串和dojo.preventCache随机串,用于缓存不同节点的请求响应(父节点只会在第一次被展开时到服务器端加载数据,之后都是从浏览器的缓存中读取数据,可以提高应用程序性能)。
首先我要先写一个加载树节点数据的Action类,代码如下:
pac kage t ut orial;
impo rt java.ut il.Map;
impo rt c om.goog lec ode.json plug in.JS O NE xept io n;
impo rt c om.goog lec ode.json plug in.JS O NUt il;
pub lic c lass AjaxT reeAc t ion ext ends Dyna mic T reeAc t ion {
privat e st at ic final lo ng seria lVers ion UID = 39700197517409423 11L;
privat e St ring ac t ion;
privat e St ring dat a;
privat e FileWra ppe r[] wrap pers;
public void set Act ion(St ring ac t ion) {
t his.act ion = ac t ion;
}
public void set Dat a(St ring dat a) {
t his.dat a = dat a;
}
public FileW rap per[] get Wra ppe rs() {
ret urn wrappe rs;
}
@Override
public St rin g exec ut e() {
if("get Child ren".e qua ls(ac t ion)) {
t ry{
Objec t o = JSO NUt il.dese ria lize(dat a);
St ring pat h = ((Map) ((Ma p) o).get("node")).get("ob je ct Id").t oSt ring();
wrappers = new FileW rap per(pat h).get Child ren();
} c atc h(JSONE xept io n e) {
e.print St ac kT rac e();
}
ret urn"aja x";
}
ret urn super.e xec ut e();
}
}
清单 6 s rc/tutori al/Aj axTreeActi on.j ava
上述代码可能需要解释一下:
1.action属性对应于XHR中的action,如果它为“getChildren”时,则
需要进行加载子节点操作。否则,会读取树的根节点,并返回JSP页
面;
2.通过上面XHR的分析,大家可以知道data是代表树和当前节点的J
SON串,故应将其反串行化为Map对象,并将其objectId属性取出。
通常情况下,Dojo树的objectId属性代表服务器端的对象的标识,
在本例中为文件夹的绝对路径;
3.wrappers属性表示当前文件夹下的文件数组,它被传送到Freemar
ker页面,翻译为Dojo树节点数组的JSON串。
下面是Freemarker页面的代码:
[
<#list wrappe rs as r>
{ "t it le": "${r.na me}", "isFolde r": <#if r.c hild ren?s ize gt0>t rue <#else>fa lse#if>, "id": "${r.id}", "ob jec t Id": "${r.abso lut ePat h?js _st rin g}" }<#if r_has_ne xt>,#if>
#list>
]
清单7 WebContent/Aj axTree.f tl
以上代码中<#lis t>#ls it>的写法是Fr eemark er中遍历集合的写法;而<#if r.ch ildren?s ize gt 0>判断“r”对象的ch ildren属性是否为空;r.ab s olutePath?js_string 就是将“r”的abs olutePath属性的值输出为Jav as cri pt 的字串符形式;<#if r_has_nex t>#if>判断集合是否有下一项数据。如果希望更详细地了解Freemarker的使用,请参考该手册。
接下来,让我们看看Action的配置代码片段:
清单8 s rc/s truts.xm l配置片段
最后是JSP页面代码:
<%@ page la ngua ge="java" c ont ent T ype="t ext/ht ml; c harset=ut f -8"
pageEnc oding="ut f-8"%>
<%@ t ag lib prefix="s" uri="/st rut s-t ags"%>
/*
funct ion t reeNodeSe lec t ed(arg) {
alert(arg.sou rc e.t it le + ' selec t ed');
}
dojo.addO nLoa d(func t ion() {
var t= do jo.widget.byId('appF iles');
var s = t.select or;
dojo.event.c onnect(s, 'select', 't reeNodeSe lec t ed');
});
/*]]> */
AJAX T ree Exa mp le
/*
dojo.requ ire("do https://www.doczj.com/doc/1517314304.html, ng.*");
dojo.requ ire("do jo.widget.*");
dojo.requ ire("do jo.widget.T ree");
dojo.requ ire("do jo.widget.T reeRPC Cont ro ller");
/*]]> */
DNDc ont ro lle r="c reat e"RPC Url="
c ont roller="t ree Cont ro ller"> widget Id=' isFolder=' objec t Id='
清单9 WebContent/Aj axTree.js p
由于上面所提及的原因,我在上述的代码中并没有使用
发布运行应用程序,在浏览器地址栏中键入http://localhos t:8080/Stru ts2_Ajax2/Ajax Tree.action,点开某个节点,在节点加载的过程中,加号图
标变成时钟状图标,如下图所示页面:
图2 AJ AX树示例
自定义
Struts2的标志过人之外在于它允许开发人员自定义标志的页面输出。要做到这一点,你所需要做的只是创建一个自定义的theme并将其应用到相应标志。下面就让我自定义一个真正的AJAX的
首先,你的源文件的根目录下新建包“temp late.realajax”。
然后,在上一步所建的包中新建“tree.ftl”文件,内容如下:
/*
dojo.requ ire("do https://www.doczj.com/doc/1517314304.html, ng.*");
dojo.requ ire("do jo.wid get.*");
dojo.requ ire("do jo.wid get.T ree");
dojo.requ ire("do jo.wid get.T reeRPC Cont ro lle r"); <#--Added by Max -->
/*]]> */
<#--Added by Max -->
widget Id="${pa ra met ers.id?ht ml}_c ont roller" DNDc ont roller="c reat e" RPC Url="<@s.url />">
<#--End -->
<#if pa ra met ers.bla n kIc onSrc?e xist s> gridIc onS rc T="<@s.u rl value='${pa ra met ers.bla n kIc onS rc}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc L?e xist s> gridIc onS rc L="<@s.url value='${pa ra met ers.g ridIc onS rc L}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc V?exist s> gridIc onS rc V="<@s.u rl value='${pa ra met ers.grid Ic onSrc V}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc P?exist s> gridIc onS rc P="<@s.u rl value='${pa ra met ers.grid Ic onSrc P}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc C?e xist s> gridIc onS rc C="<@s.u rl value='${pa ra met ers.g rid Ic onSrc C}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc X?exist s> gridIc onS rc X="<@s.u rl value='${pa ra met ers.grid Ic onSrc X}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc Y?exist s> gridIc onS rc Y="<@s.u rl value='${pa ra met ers.grid Ic onSrc Y}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.grid Ic onSrc Z?exist s> gridIc onS rc Z="<@s.u rl value='${pa ra met ers.grid Ic onSrc Z}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.expa nd Ic onSrc Plus?e xist s> expandIc onS rc Plus="<@s.u rl value='${pa ra met ers.expa nd Ic onSrc P lus}' inc ludePa ra ms='none'/>" #if> <#if pa ra met ers.expa nd Ic onSrc Min us?e xist s> expandIc onS rc Minus="<@s.url value='${para met ers.e xpan dIc onS r c Minus?ht ml}' inc ludePa ra ms='no ne'/>" #if> <#if pa ra met ers.ic onWidt h?exist s> ic onWidt h="<@s.u rl value='${pa ra met ers.ic onWidt h?ht ml}' enc od e="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.ic onHe ig ht?exist s> ic onHe ight="<@s.u rl va lue='${pa ra met ers.ic onHe ig ht?ht ml}' enc o de="fa lse"inc ludePa ra ms='no ne'/>" #if> <#if pa ra met ers.t oggle Durat io n?e xist s> t oggle Du rat ion=${pa ra met ers.t oggle Du rat ion?c} #if> <#if pa ra met ers.t emplat eCssPat h?e xist s> t emp lat eCssPat h="<@s.url value='${pa ra met ers.t e mp lat eCssPat h} ' enc ode="false"inc lu dePara ms='no ne'/>" #if> <#if pa ra met ers.show Grid?e xist s> showGrid="${pa ra met ers.show Grid?defau lt(t rue)?st ring}" #if> <#if pa ra met ers.show Root Grid?exist s> showRoot Grid="${pa ra met ers.show Root Grid?defau lt(t rue)?st ring}" #if> <#if pa ra met ers.id?e xist s> id="${pa ra met ers.id?ht ml}" #if> <#if pa ra met ers.t reeSelec t edT opic?exist s> publis hSelec t ionT opic="${pa ra met ers.t reeSelec t edT opic?ht ml}" #if> <#if pa ra met ers.t reeExpande dT opic?exist s> publis hE xpa ndedT opic="${para met ers.t reeE xpande dT opic?ht ml}" #if> <#if pa ra met ers.t reeCollapsedT op ic?exist s> publis hCo lla psedT opic="${pa ra met ers.t reeCo lla psedT opic?ht ml}" #if> <#if pa ra met ers.t oggle?e xist s> t oggle="${para met ers.t ogg le?ht ml}" #if> c ont roller="${para met ers.id?ht ml}_c ont ro lle r" <#--Adde d by Ma x --> > <#if pa ra met https://www.doczj.com/doc/1517314304.html,be l?e xist s> <#if pa ra met ers.node IdP rope rt y?exist s> id="${st ac k.findVa lue(pa ra met ers.node IdP rope rt y)}" <#else> id="${pa ra met ers.id}_root" #if> > <#else if para met ers.root Node?e xist s> ${st ac k.push(pa ra met ers.root Node)} <#-- Edit ed by Max --> t it le="${st ac k.findValue(pa ra met ers.nodeT it leP rope rt y)}" widget Id="${st ac k.findVa lue(para met ers.node IdP rope rt y)} " isFolder="<#if st ac k.findVa lue(pa ra met ers.c hildCo llec t ionP rope rt y)?size gt0>t rue<#e lse>fa lse#if>" object Id="${st ac k.findVa lue(para met ers.na meVa lue)}">
<#-- End -->
<#assig n old Node = st ac k.pop()/><#--pop t he node off of t h e st ac k, but don't show it-->
#if>
清单10 s rc/tem pl ate/real aj ax/tree.f tl
对上述稍作解释,上述代码主要在原版的s rc/template/ajax/tree.ft l的基础上添加了TreeRPCController的控件,并只输出根节点。由于
接着新建tree-clos e.ftl文件,内容和原版的一样,如下所示:
<#if pa ra met https://www.doczj.com/doc/1517314304.html,be l?e xist s>#if>
清单11 s rc/tem pl ate/real aj ax/tree-cl os e.f tl
再下来就应该是将theme应用到
<%@ page la ngua ge="java" c ont ent T ype="t ext/ht ml; c harset=ut f -8"
pageEnc oding="ut f-8"%>
<%@ t ag lib prefix="s" uri="/st rut s-t ags"%>
"ht t p://https://www.doczj.com/doc/1517314304.html,/T R/xht ml1/DT D/xht ml1-t ransit io nal.dt d">
/*
funct ion t reeNodeSe lec t ed(arg) {
alert(arg.sou rc e.t it le + ' selec t ed');
}
dojo.addO nLoa d(func t ion() {
var t= do jo.widget.byId('appF iles');
var s = t.select or;
dojo.event.c onnect(s, 'select', 't reeNodeSe lec t ed');
});
/*]]> */
AJAX T ree Exa mp le
nodeT it lePropert y="na me"node IdP rope rt y="id" c hildCo llec t ionPrope rt y="c hil d ren"value="abso lut ePat h" />
清单12 WebContent/Aj axTreeThem e.j s p
上述代码中
为了不影响前一个例子,我们为该JSP文件配置类型相同的Action,如下代码所示:
清单13 s rc/s truts.xm l配置片段
发布运行应用程序,在浏览器地址栏中键入http://localhos t:8080/Stru ts2_Ajax2/Ajax TreeTheme.action,结果如图2所示。
总结
通过上述例子,大家知道Struts 2 的AJAX 标志是基于Dojo控件开发的,所以如果大家希望熟练地使用这些标志,最好去了解一下Dojo。
在很多Web应用中,为了完成不同的工作,一个HTML form标签中可能有两个或多个submit 按钮,如下面的代码所示: