Recipe3.9.Generating Dynamic Select List Options


Recipe 3.9. Generating Dynamic Select List Options

Problem

You want to dynamically change the options displayed in a select element based on a change in another field in the same form, without having to render the set of options in client-side JavaScript.

This problem isn't about avoiding JavaScript altogether; instead, it shows how to call a Struts action from a client-side JavaScript event listener.


Solution

Use an onchange or onclick JavaScript listener to call a JavaScript function that submits the form to a Struts Action. In the Action, perform the necessary business logic to construct a new collection for the select options, and forward control back to the original JSP page. Example 3-11 shows a JSP page that submits the form to an Action when the user clicks a radio button. The value of the radio button is passed to the Action as a request parameter.

Example 3-11. Submitting a form using 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>Struts - 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>

Discussion

When the requirements for the dynamic interaction of a web page are driven by business logic, then it's best to use an Action, not JavaScript, to perform this function. Encoding business rules into JavaScript functions leads to hard-to-maintain, nonreusable code. A better approach is to execute the behavior on the server-side.

This recipe addresses the same problem as described in Recipe 3.8. However, the Solution here doesn't rely on the incorporation of the data in a JavaScript function. Instead, the function called by the onclick event handler submits the form to a different URL and Action than specified by the form's action attribute. This alternative URL directs control to an Action whose sole purpose is to determine the new set of options to display in the select control. This Action forwards control back to the original JSP page at which point the drop-down menu is populated based on the new values.

Creating a separate Action for changing the values in an HTML control may seem like overkill. However, the technique demonstrated here provides a flexible solution that puts the full power of the server behind the dynamic HTML. Consider the case where you are computing financial data for one field based on the values from another input field on the same form. The service for performing the calculation should be called by an Action. The Solution shown here works well for this scenario.

For a concrete example, the approach used in Recipe 3.8 will be replaced with the approach detailed in this recipe. This example provides an input form where a user can input information about his favorite programming language and IDE. The options for the IDE are contingent upon the selected programming language. Example 3-12 shows the JSP page (favorite_language2.jsp) that displays the form.

Example 3-12. Submitting a form to an alternate URL
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html> <head>   <title>Struts - 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>

The action elements in the struts-config.xml file specifies the URL paths used by the form. The first mapping, /FavoriteLanguage2, specifies the action that forwards to the JSP in Example 3-12. The second mapping, /GetIdeOptions, specifies the action that's called when the user clicks the radio button. The last mapping, /ViewFavoriteLanguage, specifies the action that processes the form when Submit is pressed:

<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"/>

The last piece of the puzzle is the GetIdeOptionsAction itself, shown in Example 3-13.

Example 3-13. Action for alternate URL
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"));     } }

This class is responsible for retrieving the selected language from MyForm. The Action then sets the collection containing the corresponding IDE names into the form. For simplicity, this Action creates the collections directly. In a real-world application, these values would probably come from the business layer, perhaps from a database. Finally, the Action returns the success forward, looping back to the initial Action.

A consequence of using this approach is that you may need to define the ActionForm to be in session scope. This will allow the main JSP page to reflect the changed data when the form is resubmitted back to the original page from the alternate Action.


For this example, the built-in ForwardAction processes the form, forwarding the request directly to the JSP page. If instead you were using a custom Action, consider extending the DispatchAction and implementing the ancillary action as a method of the DispatchAction. This approach allows you to keep related code together, making the application easier to maintain.

See Also

Recipe 3.8 provides an alternative solution to this problem that utilizes dynamically generated JavaScript arrays. The DispatchAction is covered in Recipe 6-8.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net