最近忙,好久沒來這里寫東西了。今天抽點時間繼續。
上周去北京,坐火車去,在火車上閱讀《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式講解。