|
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 index 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 index 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> DiscussionYou 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 wicked." Take a pragmatic approach: If JavaScript makes your application better and your users happier, then use it. But, use it in such a way that your business logic stays in the business layer, and not slapped on the web page. Struts helps you do just this. A concrete example can illustrate this approach. Suppose you want to ask a user to select his favorite programming language and, subsequently, favorite Integrated Development Environment (IDE) for the selected language. The language will be chosen using radio buttons, and the IDE will be selected from a drop-down menu. If the language is Java, then the IDE drop-down will display options such as Eclipse, Net Beans, IDEA, etc. If the language is C#, then the drop-down will display Visual Studio and SharpDevelop. Example 3-9 shows the action form that holds this data. Example 3-9. ActionForm for favorite language/IDEpackage 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 renders the input page. This example is similar to the Solution. 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 index 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 index 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# IDEs. Each array contains a set of Option JavaScript objects. The Option object represents an option of an HTML select control. This object takes four parameters in the constructor: the text value to display, the value to pass when the form is submitted, a Boolean indicating if the value is the default selected value, and another Boolean indicating if the value is currently selected. 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 populated with the Option objects representing the Java IDEs. Otherwise, the control is populated with the Option objects representing the C# IDEs. The HTML body contains the form, rendered using the Struts html tags. The Struts tags support the JavaScript change listeners via the onfunction 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 StrutsOnce 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 menuSimilarly, 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. |
|