Recipe 3.8. Dynamically Changing Select Options Using JavaScriptProblemYou want to use JavaScript to dynamically change the items displayed in an HTML select element based on data retrieved from your application's model. SolutionUse the Struts logic:iterate tag to create JavaScript arrays for the different option sets. Then use a JavaScript onchange event handler to change the options set at runtime. Example 3-8 shows a complete JSP page where the JavaScript arrays are dynamically created using Struts tags. The changeOptions event handler function resets the options for the select control using the JavaScript arrays. Example 3-8. Generating DHTML using Struts
<%@ 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>Struts - JavaScript Example</title>
<script language="JavaScript">
// Create the array for the first set of options
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>
// Create the array for the second set of options
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 is the triggering control
// baz is the select control
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>
Discussion
You can use Struts to generate JavaScript as you can use it to generate HTML. Some developers consider JavaScript "evil"; in reality, it's only "slightly
A concrete example can
Example 3-9 shows the action form that holds this data. 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 shows the JSP (
favorite_language.jsp
) that
Example 3-10. JSP Page using Struts-rendered DTHML
<%@ 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>Struts - JavaScript Example</title>
<script language="JavaScript">
// Create the array for the first set of options
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>
// Create the array for the second set of options
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>
The
script
block nested in the
head
element contains the JavaScript. The
logic:iterate
tags loop over JavaBean properties to create two JavaScript arrays: one for the Java IDEs and one for the C#
The JavaScript function for changing the options comes after the
logic:iterate
loop. This function is pure static JavaScript. The radio button that triggers the change is passed as the parameter to the function. If the current value of the radio button control is Java, then the
select
control is
The HTML body contains the form, rendered using the Struts html tags. The Struts tags support the JavaScript change listeners via the on function attributes. For radio buttons, the onclick listener works well. The single parameter passed to the function, this , is a reference to the HTML radio button. When the page is initially rendered, the display should look something like Figure 3-1. Figure 3-1. Form using DHTML and Struts
Once you click one of the radio buttons, the options in the drop-down list for the IDE field are populated with the data originally from the form bean. Figure 3-2 shows the display when you click the Java radio button. Figure 3-2. Dynamically rendered drop-down menu
Similarly, if you click the C-Sharp radio button, the values in the drop-down list change to reflect the values from the corresponding JavaScript array. JSTL can be used instead of the Struts bean and logic tags. In this case, you use the JSTL c:forEach and c:out tags instead of logic:iterate and bean:write . These tags generate the JavaScript array in the same manner as the Struts tags:
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 programming can be frustrating, particularly for the Java developer used to strong typing and compile-time checks. However, providing this type of dynamic client-side interaction can lead to a much richer end-user experience. See AlsoJavaScript: The Definitive Guide by David Flanagan (O'Reilly) is invaluable when it comes to JavaScript programming. If business logic is required to determine the dynamic data, then Recipe 3.9 provides a better approach. |