출처 : http://www.ibm.com/developerworks/java/library/j-sr4.html
Dynamic radio buttons with Struts
Create radio buttons with dynamically selected elements
Summary: Follow along as Struts Recipes co-author Danilo Gurovich walks you through five easy steps for creating radio buttons with dynamically selected elements.
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.
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:
- A fake data class to hold the
mountains
andselectedMountain
data. - A form bean containing a
String[]
array for the radio buttons and aString
for the specifically selected button. - A JSP with a form that shows the radio buttons in the required state.
- A JSP to display elements selected in the form.
- 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.
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.
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")); } } |
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.
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/> |
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.