級別: 中級
Danilo Gurovich (dan@gurovich.com), 首席工程師, Earthlink Inc.
2005 年 12 月 05 日
隨著 Struts Recipes 一書的合著者 Danilo Gurovich 一道經歷使用動態選擇的元素輕松創建單選按鈕的五個步驟。
這個訣竅跟在我上一篇文章 “Struts 的動態復選框” 之后。像動態復選框一樣,帶有動態選擇元素的單選按鈕可以給 Web 頁面添加許多麻煩,而使用 Struts,也可以很容易地創建它們。
在這篇文章中,我將介紹通過嵌套 Struts 標記 <logic:iterate/> 和 <html:radio/> 創建單選按鈕組。然后我用一個特定的表單 bean 指向這個標記并對保存單選按鈕值的 String[] 數組進行迭代,把相同的 name 屬性分配給每個 value 屬性。
請參閱 下載區 獲得完整的示例源代碼;如果需要下載 Struts 框架,請參閱 參考資料。
五步訣竅
為了簡單起見,我用與上次在動態復選框中使用的相同工作示例來演示單選按鈕。我的簡單的用戶界面使用單選按鈕元素來顯示喜瑪拉雅山頂峰高度的 String[] 數組,第二個 selectedMountain String 數組代表選中的單選按鈕。在創建了按鈕之后,調用一個 JavaScript 函數對單選按鈕進行預選。
根據喜瑪拉雅這個示例,用 Struts 創建動態選擇的單選按鈕的訣竅包含五個部分:
- 一個假數據 類,其中容納
mountains 和 selectedMountain 數據。
- 一個表單 bean,包含單選按鈕的
String[] 數組和特意選中的按鈕的 String 。
- 一個帶有表單的 JSP,顯示處于所需狀態的單選按鈕。
- 一個 JSP,顯示表單中選中的元素。
- 一個
Action 類,從表單頁面轉到顯示頁面。
單選按鈕訣竅和上次學的動態復選框訣竅的主要區別是:Struts 沒有提供自動創建 selected 值的工具,而這通常是創建動態單選按鈕所需要的。雖然用內置在 Struts JSP 標記中的功能預先選中 checkbox 和 select 輸入類型相當簡單,但是 radio button 輸入類型要求不同的解決方案。幸運的是,使用來自表單 bean 的信息和幾行 JavaScript 代碼就可以實現這個要求,如第 3 步所示。
第 1 步. 創建數據層
我做好了一個假的數據類,將來自業務層的數據呈現到應用程序視圖層,視圖層是我要考慮的事。名為 FakeData 的類包含兩個靜態方法,如清單 1 所示:
清單 1. FakeData.java
/**
* class FakeData -- represents the business logic
*/
public class FakeData {
/**
* data for mountains
*/
public static final String[] MOUNTAINS =
{"Everest", "K2", "Kangchenjunga",
"Lhotse", "Makalu", "Cho Oyu"};
/**
* data for selected mountain
*/
public static final String SELECTED_MOUNTAIN = "Kangchenjunga";
}
|
創建假的數據層是一種有用的用戶界面開發實踐,因為最終應用程序要使用的持續存儲層對于前端開發人員來說,通常是看不到的。所以沒有必要等待后臺團隊把工作完成,可以方便地開發一個假的數據層來模擬最終要發送過來的 API 和功能。使用假數據層,可以進行用戶界面開發并降低對其他團隊的依賴。有了假數據層,還可以定義到項目其他部分的 API 連接,并確保在集成所有部分的時候問題較少。
第 2 步. 創建表單 bean
最終要填充應用程序的值可能來自比清單 1 所示的框架復雜得多的框架。對于更漂亮的示例,有一個好消息,就是清單 2 中的表單 bean 不必做任何繁重的工作,所以它只是一個帶有 getter 和 setter 方法的簡單 Java™ 對象。實際的值是在調用構造函數時插入的。
清單 2. RadioTestForm.java
package com.strutsrecipes;
import org.apache.struts.action.ActionForm;
/**
* Radio Button Test Form to show an array of radio buttons and
*/
public class RadioTestForm extends ActionForm {
// ------------------------------ FIELDS ------------------------------
/**
* The selected Mountain
*/
private String selectedMountain;
/**
* The list of mountains for the radio button
*/
private String[] mountains;
// --------------------------- CONSTRUCTORS ---------------------------
/**
* Constructor -- using FakeData...
*/
public RadioTestForm() {
this.selectedMountain = FakeData.SELECTED_MOUNTAIN;
this.mountains = FakeData.MOUNTAINS;
}
// --------------------- GETTER / SETTER METHODS ---------------------
/**
* Getter for the mountains
*
* @return the mountains array
*/
public String[] getMountains() {
return this.mountains;
}
/**
* Setter for the mountains
*
* @param m the Mountains array
*/
public void setMountains(String[] m) {
this.mountains = m;
}
/**
* Getter for selectedMountain
*
* @return the selected mountain
*/
public String getSelectedMountain() {
return this.selectedMountain;
}
/**
* Setter for selectedMountain
*
* @param sm the selectedMountain
*/
public void setSelectedMountain(String sm) {
this.selectedMountain = sm;
}
}
|
為了清晰起見,我包含了表單 bean 的所有 Java 代碼。注意,Kangchenjunga 同時列出在 selectedMountain 和 mountains 字段中,在構造函數中實例化,并用 FakeData 類來填充。現在,我已經有了足夠的信息,可以把 Kangchenjunga 傳遞到 JSP,作為 preselected 的初始值。
第 3 步. 創建單選按鈕 JSP
清單 3 包含表單頁面的 JSP 代碼,表單頁面中包含單選按鈕和預選的值。請注意 Java 文件和邏輯、HTML、bean 標記與表單底部的 JavaScript 函數之間的關系。我對 mountains 集合進行迭代,以創建單選按鈕。這個工作完成之后,我添加 JavaScript 并將 selectedMountain 的值填充到其中,并與單選按鈕數組進行比較,以選擇正確的按鈕。
清單 3. 包含單選按鈕和預選值的 JSP
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<html:html locale="true">
<head>
<title><bean:message key="testForm.title"/></title>
<html:base/>
</head>
<body>
<h3><bean:message key="testForm.heading"/></h3>
<html:form action="/FormAction" name="testForm"
type="com.strutsrecipes.RadioTestForm">
<h4><bean:message key="testForm.instruction"/></h4>
<!-- gets the selected radio button -->
<bean:define id="selectedRadio" property="selectedMountain" name="testForm"/>
<!-- creates the radio button list -->
<logic:iterate id="mountain" property="mountains" name="testForm">
<%-- you need this hack to get the value of the mountains to the page --%>
<bean:define id="mountainValue">
<bean:write name="mountain"/>
</bean:define>
<html:radio property="selectedMountain"
value="<%=mountainValue%>"
styleId="<%=mountainValue%>"/>
<bean:write name="mountain"/><br/>
</logic:iterate><br/>
<html:submit/> <html:reset/>
<script type="text/javascript">
<!--
//Either of the following works.
//Uncomment the one you wish to try and comment the other out.
//var selectedRadio =
document.getElementById("<bean:write //name="selectedRadio"/>");
var selectedRadio =
document.forms["testForm"].elements["<bean:write name="selectedRadio"/>"];
selectedRadio.checked=true; -->
</script>
</html:form>
</body>
</html:html>
|

 |

|
第 4 步. 創建顯示頁面
顯示頁面基本與復選框訣竅中使用的頁面相同,區別只是不需要迭代,因為只選中了一個值。需要做的只是列出 bean 中選中的 mountain ,如清單 4 所示:
清單 4. 顯示頁面的 JSP
<%@taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%>
<%@taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%>
<%@taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic"%>
<%-- html code, etc... --%>
<bean:write name="mountain"/><br/>
<hr size=5 color="black"/>
<%-- some more html code, etc... --%>
|
第 5 步. 編寫 Action 類
正像我在 前一篇文章 提到過的,Action 類比起這個訣竅中的其他組件來說,做的工作并不多。在這里要做的僅僅是得到 String 數組和 selectedMountain 的值,并讓顯示頁面能夠使用它們。
清單 5. Action 類
ipackage com.strutsrecipes;
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 javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* The Action for the Radio Button test
*/
public final class RadioTestAction extends Action {
// -------------------------- OTHER METHODS --------------------------
/**
* The Action for the Radio Button test
*/
public ActionForward execute(ActionMapping mapping,
ActionForm form, HttpServletRequest request, HttpServletResponse response
)
throws ServletException, Exception {
// Extract attributes needed
String selectedMountains = ((RadioTestForm) form).getSelectedMountain();
System.out.println("htmlString RETURNED*********\n" + selectedMountains);
//Save your htmlString in the session
HttpSession session = request.getSession();
session.setAttribute(Constants.MOUNTAINS, selectedMountains);
return (mapping.findForward("success"));
}
}
|

 |

|
訣竅內部
訣竅背后真正起作用的是 JavaScript。首先,我在清單 5 中,為 JSP 表單內的 selectedMountain 字段定義了一個 JSP 腳本變量,如下所示:
<bean:define id="selectedRadio" property="selectedMountains" name="testForm"/>
|
在表單下面,我創建了一個 JavaScript 函數,包含以下兩行:
var selRadio = document.getElementById("<bean:write name="selectedRadio"/>");
selRadio.checked=true;
|
在客戶端腳本內部,我創建了 selRadio JavaScript 變量然后對文檔內的所有元素進行查找(find),查找 id (或者預編譯代碼中的 styleId )與 selectedRadio 變量匹配的元素。我通過把 <html:radio/> 標記的 styleId 屬性設置成與它的名稱/值匹配來做到這點。在 JavaScript 函數迅速地在頁面上的 ID 之間迭代的時候,只是把單一單選按鈕設置為 selected。
替代方法
用如下所示的 JavaScript 方法也能產生同樣效果:
var selectedRadio =
document.forms["testForm"].elements["<bean:writename="selectedRadio"/>"];
selectedRadio.checked=true;
|
這個腳本只區分表單元素的 name 屬性而不是 id 屬性。兩種實現都可以工作,所以具體選擇哪一種取決于個人需要和偏好。JSP 表單頁面的實際輸出看起來像清單 6:
清單 6. JSP 表單頁面的輸出
<input type="radio" name="selectedMountain" value="Everest" id="Everest"/>
Everest<br/>
<input type="radio" name="selectedMountain" value="K2" id="K2"/>
K2<br/>
<input type="radio" name="selectedMountain"
value="Kangchenjunga" checked="checked" id="Kangchenjunga"/>
Kangchenjunga<br/>
<input type="radio" name="selectedMountain" value="Lhotse" id="Lhotse"/>
Lhotse<br/>
<input type="radio" name="selectedMountain" value="Makalu" id="Makalu"/>
Makalu<br/>
<input type="radio" name="selectedMountain" value="Cho Oyu" id="Cho Oyu"/>
Cho Oyu<br/>
|
結束語
正像在上一篇中的結束語中我提到過的,Struts 中關于創建動態元素的信息的缺乏,是我與 George Franciscus 合著 Struts Recipes 的一個強烈動機。關于創建動態單選按鈕的信息的缺乏很大程度上是因為它固有的設計缺陷:服務器端和客戶端的信息和功能耦合得太緊密。使用 JavaScript 和服務器端變量,就像我在這里演示的這樣,似乎是提供這個功能的惟一方式,但是仍然要冒著客戶機和服務器對彼此知道得太多的風險。
是否要冒 “耦合” 的風險的決策要您自己來決定。做這項決策的一個好過程是向自己解釋它的利弊。如果解釋之后還愿意冒這個風險 —— 而且同伴也不笑話您,那么就可以冒險了。
|