출처 : http://www.ibm.com/developerworks/java/library/j-sr4.html

Dynamic radio buttons with Struts

Create radio buttons with dynamically selected elements

Danilo Gurovich (dan@gurovich.com), Principal Engineer, Earthlink Inc.

Summary:  Follow along as Struts Recipes co-author Danilo Gurovich walks you through five easy steps for creating radio buttons with dynamically selected elements.

This recipe follows on the heels of what you learned in my last article, "Dynamic checkboxes with Struts." Like dynamic checkboxes, radio buttons with dynamically selected elements can add a lot of sophistication to your Web pages, and with Struts, they're also easy to create.

In this article, I show you how to create a radio button group by nesting the Struts tags <logic:iterate/> and <html:radio/>. I then reference a specified form bean to the tags and iterate through a String[] array of radio button values, assigning an identical name attribute to each assigned value attribute.

See the Download section for the complete example source code; see Resources if you need to download the Struts framework.

A five-step recipe

For the sake of simplicity, I use the same working example for radio buttons as I used last time for dynamic checkboxes. My simple UI uses radio button elements to display a String[] array of mountain peaks in the Himalayas, and a second selectedMountain String to represent the selected radio button. The radio button is preselected by a JavaScript function invoked after the buttons have been created.

Based on the Himalayas example, the recipe for creating dynamically selected radio buttons in Struts consists of five pieces:

  1. A fake data class to hold the mountains and selectedMountain data.
  2. A form bean containing a String[] array for the radio buttons and a String for the specifically selected button.
  3. A JSP with a form that shows the radio buttons in the required state.
  4. A JSP to display elements selected in the form.
  5. A simple Action class to go from the form page to the display page.

The major difference between the radio button recipe and the dynamic checkboxes recipe you learned last time is that Struts provides no automatic facility for creating a selected value, which is usually needed to create dynamic radio buttons. While it's fairly simple to preselect checkbox and select input types using functionality built into the Struts JSP tags, the radio button input type requires a different solution. Fortunately, this is readily accomplished using information from a form bean and a couple of lines of JavaScript, as I show in Step 3.


Step 1. Create the data layer

I've assembled a fake data class to represent data coming from a business layer to the application's view layer, which is my concern. The appropriately named FakeData class contains two static final methods, as shown in Listing 1:


Listing 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";

}

Creating a fake data layer is a useful UI development practice because the persistence layer to be used by the final application is often unavailable to front-end developers. Instead of waiting around for the backend team to do its stuff, you can easily develop a fake data layer that mimics the API and functionality that will eventually be sent to you. With the fake data layer, you can proceed with your UI development and lowers your dependence on other teams. Because of the fake data layer, you also can define your API connections to other parts of the project and ensure you have less problems when it's time to integrate all the pieces.


Step 2. Create the form bean

The values that will ultimately populate your application are likely to come from more sophisticated framework than the one shown in Listing 1. The nice thing about my more bohemian example is that the form bean in Listing 2 doesn't have to do any heavy lifting, so it's just a simple Java™ object with getters and setters. The actual values are injected when the constructor is called.


Listing 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;
    }
}

For clarity, I've included all of the Java code for the form bean. Notice that Kangchenjunga is listed in both the selectedMountain and mountains fields, instantiated in the constructor, and populated by my FakeData class. At this point, I already have enough information to propagate Kangchenjunga to the JSP as my preselected initial value.


Step 3. Create the radio button JSP

Listing 3 contains the JSP code for the form page containing radio buttons and my preselected value. Note the correlation between the Java files and the logic, HTML, and bean tags, and the JavaScript function located at the bottom of the form. I iterate through the mountains collection to create the radio buttons. Once this is done, I add the JavaScript and populate the value of selectedMountain into it for comparison against the array of radio buttons to select the proper one.


Listing 3. The JSP containing radio buttons and a preselected value
<%@ 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>


Step 4. Create the display page

The display page is nearly identical to the one used for the checkbox recipe, except no iteration is needed because only one value is selected. All I need to do is list the mountain selected in the bean, as shown on Listing 4:


Listing 4. The JSP for the display page.
<%@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... --%>


Step 5. Write the Action class

As I mentioned in my previous article, the Action class doesn't do much compared to the other components of this recipe. All I'm doing here is getting my String array and selectedMountain value and making them available to my display page.


Listing 5. The Action class
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"));
    }
}


Inside the recipe

The real muscle behind this recipe is JavaScript. First, I defined a JSP scripting variable for the selectedMountain field in Listing 5 inside the form on the JSP, as shown here:

<bean:define id="selectedRadio" property="selectedMountains" name="testForm"/>

Below the form, I created a JavaScript function consisting of the following two lines:

var selRadio = document.getElementById("<bean:write name="selectedRadio"/>");
selRadio.checked=true;
	   

Inside the client-side script, I created a selRadio JavaScript variable and then did a find for all the elements in the document whose id (or styleId in the precompiled code) matched the selectedRadio variable. I accomplished this by setting the <html:radio/> tag's styleId attribute to match its name/value. While the JavaScript function quickly iterates through the IDs on the page, it simply sets a singular radio button as selected.

An alternative approach

You could produce the same results using the JavaScript method shown here:

		
var selectedRadio =
document.forms["testForm"].elements["<bean:writename="selectedRadio"/>"];
selectedRadio.checked=true;

This script discriminates to only the form element's name attribute instead of the id. Either implementation works, so the one you choose will depend on your requirements and preferences. The actual output from the JSP form page looks like Listing 6:


Listing 6. The output from the JSP form page
<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/>


In conclusion

As I mentioned in the conclusion to my last article, the lack of information about creating dynamic elements in Struts was a strong motivation for co-writing Struts Recipes with George Franciscus. Much of the silence about creating dynamic radio buttons is because of the design dilemma inherent in too tightly coupling server-side and client-side information and functionality. Using JavaScript and server-side variables, as I've demonstrated here, seems to be the only way to harness this functionality, although you still run the risk of the client and server knowing too much about each other.

The decision of whether to take the risk of "coupling" is yours to make. A good process for making the decision is to explain the pros and cons out loud to yourself. If you're still willing to take the risk after that -- and if the person in the next cube isn't laughing at you -- you're probably ready to take the plunge.

+ Recent posts