第3.8式. 使用JavaScript動態(tài)改變選擇項
問題
你希望使用JavaScript 來根據(jù)從應(yīng)用模型中獲得的數(shù)據(jù)來動態(tài)設(shè)置顯示在一個HTML select元素中的條目。
動作要領(lǐng)
使用Struts logic:iterate標簽來為不同的選項集創(chuàng)建JavaScript 數(shù)組。然后使用JavaScript 的onchange事件句柄來在運行時修改options集。Example 3-8就展示了一個完整的JSP ,在其中JavaScript 數(shù)組是使用Struts 標簽動態(tài)創(chuàng)建的。changeOptions事件句柄函數(shù)使用JavaScript數(shù)組重設(shè)了select控件的選項.
Example 3-8. 使用Struts產(chǎn)生DHTML

<%
@ 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" %>

<%
@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html>
<head>
<title>Apache Struts Web Framework - JavaScript Example</title>

<script language="JavaScript">
// 為第一組選項創(chuàng)建一個數(shù)組
fooArray = new Array( );
<logic:iterate id="fooValue" indexId="ctr"
name="MyForm" property="fooList">
fooArray[<bean:write name="ctr"/>] =
new Option("<bean:write name='fooValue'/>",
"<bean:write name='fooValue'/>",
false, false);
</logic:iterate>

// 為第二組選項創(chuàng)建數(shù)組
barArray = new Array( );
<logic:iterate id="barValue" indexId="ctr"
name="MyForm" property="barList">
fooArray[<bean:write name="ctr"/>] =
new Option("<bean:write name='barValue'/>",
"<bean:write name='barValue'/>",
false, false);
</logic:iterate>


function changeOptions(var control)
{
// control 是觸發(fā)的控件
// baz 是select 控件
baz = document.MyForm.baz;
baz.options.length=0;

if (control.value == 'Foo')
bazArray = fooArray;
else
bazArray = barArray;

for (i=0; i < bazArray.length; i++)
baz.options[i] = bazArray[i];
}
</script>
</head>
<body>
<html:form name="MyForm" action="processMyForm">
<html:radio property="fooBar" value="Foo"
onclick="changeOptions(this);"/> Foo<br/>
<html:radio property="fooBar" value="Bar"
onclick="changeOptions(this);"/> Bar<br/>
Baz: <html:select property="baz">
</html:select>
</html:form>
</body>
</html>

動作變化
你可以使用Struts 來產(chǎn)生JavaScript,就像用它生成HTML一樣。某些開發(fā)人員認為JavaScript 簡直是"惡魔";實際上,它只是稍微有那么一點壞而已。考慮這個編程原則:如果JavaScript 能使你的應(yīng)用更好而且用戶喜歡,那就用它。但是用它的原則是,仍然將你的應(yīng)用邏輯保留在你的業(yè)務(wù)層,而不是放在頁面中。Struts 對此有幫助。
用一個具體的例子可以說明這個原則。假定你想要一個用戶選擇他喜歡的編程語言,并且,據(jù)此選擇他喜歡的IDE。語言可以通過使用單選按鈕來選擇,而IDE 將通過下拉列表來選擇。如果語言是Java, 那么IDE 下拉列表將顯示諸如Eclipse, Net Beans, IDEA, 等等。而如果語言是C#, 則下拉列表顯示Visual Studio 和SharpDevelop。
Example 3-9 展示了持有這些數(shù)據(jù)的action form。
Example 3-9. ActionForm for favorite language/IDE
package com.oreilly.strutsckbk;

import org.apache.struts.action.ActionForm;


public final class MyForm extends ActionForm
{
private static String[] javaIdes =

new String[]
{"Eclipse", "IDEA", "JBuilder",
"JDeveloper", "NetBeans"};
private static String[] csharpIdes =

new String[]
{"SharpDevelop", "Visual Studio"};

public String[] getJavaIdes( )
{return javaIdes;}

public String[] getCsharpIdes( )
{return csharpIdes;}

public String getLanguage( )
{
return language;
}

public void setLanguage(String language)
{
this.language = language;
}


public String getIde( )
{
return ide;
}

public void setIde(String ide)
{
this.ide = ide;
}
private String language;
private String ide;
}

Example 3-10 則列出了渲染輸入頁面的JSP (favorite_language.jsp) 。這個例子和基本動作中的例子有些相似。

<%
@ 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" %>

<%
@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<html>
<head>
<title>Apache Struts Web Framework - JavaScript Example</title>

<script language="JavaScript">
// 為第一組選項創(chuàng)建數(shù)組
javaIdesArray = new Array( );
<logic:iterate id="ide" indexId="ctr"
name="MyForm" property="javaIdes">
javaIdesArray[<bean:write name="ctr"/>] =
new Option("<bean:write name='ide'/>",
"<bean:write name='ide'/>",
false, false);
</logic:iterate>

// 為第二組選項創(chuàng)建數(shù)組
csharpIdesArray = new Array( );
<logic:iterate id="ide" indexId="ctr"
name="MyForm" property="csharpIdes">
csharpIdesArray[<bean:write name="ctr"/>] =
new Option("<bean:write name='ide'/>",
"<bean:write name='ide'/>",
false, false);
</logic:iterate>


function changeOptions(control)
{
ideControl = document.MyForm.ide;
ideControl.options.length=0;

if (control.value == 'Java')
ideArray = javaIdesArray;
else
ideArray = csharpIdesArray;

for (i=0; i < ideArray.length; i++)
ideControl.options[i] = ideArray[i];
}
</script>
</head>
<body>
<html:form action="/admin/ViewFavoriteLanguage">
What's your favorite programming language?<br/>
<html:radio property="language" value="Java"
onclick="changeOptions(this);"/> Java<br/>
<html:radio property="language" value="C-Sharp"
onclick="changeOptions(this);"/> C-Sharp<br/>
<p>What's your favorite development tool?<br/>
IDE: <html:select property="ide"/>
</p>
<html:submit/>
</html:form>
</body>
</html>

茜套在head元素中的script塊包含了JavaScript。logic:iterate標簽遍歷JavaBean 屬性以創(chuàng)建兩個JavaScript 數(shù)組:一個針對IDE,另一個針對C# IDE。每一個數(shù)組都包含一組Option的JavaScript 對象。一個Option對象表示HTML select控件中的一個選項。這些對象在其構(gòu)造器重使用了四個參數(shù):用來現(xiàn)實的text 值,表單提交時傳遞的值,指示該值是否是默認值的一個Boolean值,以及指示其是否是當前選擇的Boolean值。
用于修改選項的JavaScript 函數(shù)緊跟logic:iterate循環(huán)。這個函數(shù)是個純粹的靜態(tài)JavaScript。觸發(fā)修改的單選按鈕是作為參數(shù)傳遞給這個函數(shù)的。如果單選按鈕的當前值是那么e select控件則根據(jù)表達Java IDE的Option對象組裝。否則,控件使用表達C# IDE的Option對象組裝。
HTML body 則包含表單,使用Struts html標簽來渲染。Struts 標簽通過on函數(shù)屬性來支持JavaScript 的改變監(jiān)聽器。對 radio 按鈕來說,onclick監(jiān)聽器可正常工作。傳遞給這個函數(shù)的唯一參數(shù)是對HTML radio button的引用。當頁面被第一次渲染時,看起來應(yīng)該是這樣:

一旦你點擊一個單選按鈕,下拉列表中的選項便會根據(jù)來自form bean中的數(shù)據(jù)動態(tài)組裝。下圖顯示了點擊Java radio button的顯示結(jié)果:
Figure 3-2. Dynamically rendered drop-down menu

類似的,如果你點擊C-Sharp radio button,下來列表中的選項也會變化以反映對應(yīng)的JavaScript 數(shù)組中的值。
JSTL 其實也可以用于上述地方代替Struts bean和logic標簽。比如,使用JSTL c:forEach和c:out來代替logic:iterate和bean:write。這些標簽可以產(chǎn)生相同的效果。
javaIdesArray = new Array( );
<c:forEach var="ide" varStatus="status"
items="${MyForm.javaIdes}">
javaIdesArray[<c:out value="${status.index}"/>] =
new Option("<c:out value='${ide}'/>",
"<c:out value='${ide}'/>",
false, false);
</c:forEach>

JavaScript 變成可能非常令人受挫,特別是那些熟悉強類型和編譯時檢查的開發(fā)人員。但是使用這種客戶端動態(tài)交互,確實可以改善用戶體驗特征。
所以, AXAJ如今正在熱門,并結(jié)合到很多傳統(tǒng)技術(shù)之上。
相關(guān)動作
如果需要業(yè)務(wù)邏輯來決定動態(tài)數(shù)據(jù),可參考下一個動作的方法,興許更好些。