最近忙,好久没来这里写东西了。今天抽点时间继续。
上周去北京,坐火车去,在火车上阅读《Core J2EE Patern》,想起再上一次去北京,也是坐火车,也是阅读这本书,不过那次是第1版。
还有巧的是,去时铺位是16车16号,来时居然又买到16车16号,不过是上铺。真是有点意思。
Recipe 3.9. 产生动态选择列表项目
问题
你想要基于同一个表单中的另一个字段的变化,动态改变一个select元素中显示的项目,而不是非要在客户端使用JavaScript 来处理选项集。
|
这个问题并不会完全避免JavaScript;相反,它展示了如何从客户端JavaScript事件监听器中调用Struts action 的技术。 | |
动作要领
使用onchange或者onclick JavaScript 监听器来调用一个将表单提交至一个Struts Action的JavaScript 函数。在Action中,执行必要的业务逻辑来构造一个新的select选项集,并且将控制转发回原来的JSP 页面。Example 3-11就展示了当用户点击一个单选按钮时将表单提交至一个Action的JSP页面。单选按钮的值是作为一个请求参数传递给Action的。
Example 3-11. 使用JavaScript提交表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Apache Struts Web Framework - JavaScript Example</title>
<script language="JavaScript">
function getOptions(control) {
form = control.form;
form.action = "SetOptions.do?someProp=";
form.action += control.value;
form.submit( );
}
</script>
</head>
<body>
<html:form action="ProcessMyForm">
<html:radio property="someProp1" value="val1"
onclick="getOptions(this);"/> Value 1<br/>
<html:radio property="language" value="val2"
onclick="getOptions(this);"/> Value 2<br/>
SomeProp2:
<html:select property="someProp2">
<html:optionsCollection property="prop2Values"/>
</html:select>
</p>
<html:submit/>
</html:form>
</body>
</html>
动作分解
当一个Web页面的动态交互需求是业务逻辑驱动的时候,那么最好使用一个Action,而不是JavaScript,来执行这个功能。将业务逻辑编码进JavaScript 函数将导致难以维护和不可重用的代码。所以最好在服务器端执行这个行为。
这个技术也可以解决第3.8式中的同一个问题。但是,这个动作却没有依赖于JavaScript 函数中的数据。而是,被onclick时间句柄调用的函数将表单提交到一个与表单的action属性中指定的不同的另一个URL 和Action。这个替换的URL 将控制定向到专门处理显示在select控件中的新的选项集的一个Action。然后这个Action将控制转发回原来的JSP 页面,在其中使用新的值重新组装下拉列表菜单。
创建一个单独的Action来处理HTML 控件中的值的改变好像有些过分。但是,这里展示的技术提供了一个利用了动态HTML背后的服务器端的全部威力的灵活方案。考虑一下你要基于同一个表单中的另一个字段的输入值来计算某个字段的金融数据的情形。执行计算的服务就应该由Action来完成。这里所示的解决方案对这种情形就很好。
对于一个具体的例子,第3.8式所用的方法可以被这里所属的方法代替。这个例子提供了一个输入表单,从其中用户可以输入和选择其钟爱的编程语言和IDE。针对IDE的选项则依赖于编程语言的选择。Example 3-12 显示了现实这个表单的JSP 页面(favorite_language2.jsp)。
Example 3-12. 将表单提交到另一个URL
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<html>
<head>
<title>Apache Struts Web Framework - JavaScript Example</title>
<script language="JavaScript">
function getOptions(control) {
form = control.form;
form.action = "GetIdeOptions.do?language=";
form.action += control.value;
form.submit( );
}
</script>
</head>
<body>
<html:form action="ViewFavoriteLanguage">
What's your favorite programming language?<br/>
<html:radio property="language" value="Java"
onclick="getOptions(this);"/> Java<br/>
<html:radio property="language" value="C-Sharp"
onclick="getOptions(this);"/> C-Sharp<br/>
<p>What's your favorite development tool?<br/>
IDE:
<html:select property="ide">
<html:optionsCollection property="ides"/>
</html:select>
</p>
<html:submit/>
</html:form>
</body>
</html>
Apache Struts Web Framework-config.xml中的action元素指定了表单所用的URL 路径。第一个mapping,/FavoriteLanguage2,指定了转发到Example 3-12的JSP的action 。第二个mapping, /GetIdeOptions, 则指定了当用户点击单选按钮时调用的action。最后一个mapping, /ViewFavoriteLanguage, 则指定的是按下Submit 按钮时处理表单的action。
<action path="/FavoriteLanguage2"
name="MyForm"
scope="session"
type="org.apache.struts.actions.ForwardAction"
parameter="/favorite_language2.jsp"/>
<action path="/GetIdeOptions"
name="MyForm"
scope="session"
type="com.oreilly.strutsckbk.GetIdeOptionsAction">
<forward name="success" path="/FavoriteLanguage2.do"/>
</action>
<action path="/ViewFavoriteLanguage"
name="MyForm"
scope="session"
type="org.apache.struts.actions.ForwardAction"
parameter="/view_favorite_language.jsp"/>
最后是GetIdeOptionsAction本身,示于Example 3-13。
Example 3-13. 处理替代URL 的Action
package com.oreilly.strutsckbk;
import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.util.LabelValueBean;
public final class GetIdeOptionsAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
MyForm myForm = (MyForm) form;
String language = myForm.getLanguage( );
ArrayList ides = new ArrayList( );
if (language.equals("Java")) {
ides.add(new LabelValueBean("Net Beans","Net Beans"));
ides.add(new LabelValueBean("Eclipse", "Eclipse"));
ides.add(new LabelValueBean("jEdit", "jEdit"));
}
else if (language.equals("C-Sharp")) {
ides.add(new LabelValueBean("Sharp Develop", "Sharp Develop"));
ides.add(new LabelValueBean("Visual Studio", "Visual Studio"));
}
myForm.setIdes( ides );
// Forward control to the specified success URI
return (mapping.findForward("success"));
}
}
这个类负责从MyForm中获取选择的编程语言。然后Action设置包含对应的IDE名城的集合到表单中。为了简化,这个Action直接创建了集合。在实际应用中,这些值可能来自于业务层,也许是来自于一个数据库。最后,Action返回success forward,又将控制转到初始Action。
|
使用这个技术的一个后果是你可能需要将ActionForm定义在session范围中。这样可以让主JSP 页面在表单被从预备Action重新提交回原始页面时可以反映修改了的数据。. | |
对这个例子,内置的ForwardAction将处理表单,直接将请求转发至JSP页面。如果你是使用一个定制的Action,请考虑扩展DispatchAction并且实现辅助action 为DispatchAction的一个方法。这种方式将相关代码集中在一起,使应用更易维护。
相关动作
第3.8 式提供了另一个技术,它使用了动态产生JavaScript 数组的方式来解决这个问题。
DispatchAction将在第6.8式讲解。