A common problem is having two related select controls on a page. Call them the 'trigger' and the 'target'. This is an ideal use case for Ajax, because you don't want to refresh the entire page to re-populate the 'target' control when the user selects something in the 'trigger'. We have produced a very nice demo of this using the S2 Ajax tags, and I am enclosing the Action class and a jsp page which give the example. I hope this is useful to people. It was a difficult problem to solve, not helped by the very poor quality of the Ajax documentation on the S2 site (sorry for beefing, but I spent lots of extra hours because of it). The documentation is incomplete, disorganized, contradictory, and in many cases just plain wrong.
Anyway, here is the example: The page calling the demo uses a link to "/greetings/AjaxDynDoubleSelect_setup.action" Here is the mapping of the action from struts.xml: <action name="AjaxDynDoubleSelect_*" method="{1}" class="greetings.struts2.action.AjaxDDS_DemoAction" > <result name="success" type="tiles" > /greetings/AjaxDemo.jspx </result> </action> This is the Action class: package greetings.struts2.action; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import com.opensymphony.xwork2.ActionSupport; /** * @author Ray Clough, Michael Matz. * Created: May 29, 2007 * Project: GreetingsEarth_S2_S1_Tiles * Package: greetings.struts2.action * File: AjaxDDS_DemoAction.java * <br/> * Description: * <br/> * This Struts-2 Action class is used to handle the data actions for the * display and function of the Ajax Dynamic Double Select (DDS) demonstration. * The point is to use two 'select' controls, which contain related data. * The first select control, referred to here as the 'trigger', reacts to * an 'onchange' event to set the contents of the second select control, * referred to here as the 'target', to a corresponding list. The update * of the 'target' is done with Ajax, meaning that the entire page does not * need to be refreshed. This is a common, but fairly trick operation. */ public class AjaxDDS_DemoAction extends ActionSupport { /** Objects implementing 'Serializable' should define 'serialVersionUID' */ private static final long serialVersionUID = -2394017505624866795L; private Map<String,List<String>> dataModel; private String triggerVal = null; /** * This constructor initializes the data set which is displayed in * the two select controls. */ public AjaxDDS_DemoAction() { // create the data model List<String> grains = new ArrayList<String>(); List<String> fruits = new ArrayList<String>(); List<String> veggies = new ArrayList<String>(); List<String> nuts = new ArrayList<String>(); List<String> other = new ArrayList<String>(); grains.add("Wheat"); //$NON-NLS-1$ grains.add("Barley"); //$NON-NLS-1$ grains.add("Oats"); //$NON-NLS-1$ grains.add("Rye"); //$NON-NLS-1$ grains.add("Corn"); //$NON-NLS-1$ fruits.add("Apples"); //$NON-NLS-1$ fruits.add("Oranges"); //$NON-NLS-1$ fruits.add("Limes"); //$NON-NLS-1$ fruits.add("Kumquats"); //$NON-NLS-1$ fruits.add("Dates"); //$NON-NLS-1$ fruits.add("Persimmons"); //$NON-NLS-1$ veggies.add("Leeks"); //$NON-NLS-1$ veggies.add("Onions"); //$NON-NLS-1$ veggies.add("Beans"); //$NON-NLS-1$ veggies.add("Peas"); //$NON-NLS-1$ veggies.add("Carrots"); //$NON-NLS-1$ nuts.add("Pecans"); //$NON-NLS-1$ nuts.add("Pistachios"); //$NON-NLS-1$ nuts.add("Walnuts"); //$NON-NLS-1$ nuts.add("Filberts"); //$NON-NLS-1$ nuts.add("Pinole"); //$NON-NLS-1$ nuts.add("Cashews"); //$NON-NLS-1$ other.add("Ice Cream"); //$NON-NLS-1$ other.add("Chocolate"); //$NON-NLS-1$ other.add("Fudge"); //$NON-NLS-1$ other.add("Pie"); //$NON-NLS-1$ other.add("Cake"); //$NON-NLS-1$ dataModel = new LinkedHashMap<String,List<String>>(); dataModel.put("Grains", grains); //$NON-NLS-1$ dataModel.put("Fruits", fruits); //$NON-NLS-1$ dataModel.put("Vegetables", veggies); //$NON-NLS-1$ dataModel.put("Nuts", nuts); //$NON-NLS-1$ dataModel.put("Other", other); //$NON-NLS-1$ } /** * @return The 'setup()' method sets the initial value for the 'trigger', * which will result in a compatible 'target' list being displayed */ public String setup() { // initial value for TargetList this.triggerVal = "Grains"; //$NON-NLS-1$ return SUCCESS; } /** * The 'update()' method is called when the user selects a value from * the 'trigger' list. * * @return The method writes the option elements to the response * output Writer, and returns 'null'. Returning 'null' tells the * Struts processor that the processing cycle is complete, and the * page asynchronously displays the returned values. */ @SuppressWarnings("nls") public String update() { HttpServletRequest request = ServletActionContext.getRequest(); triggerVal = request.getParameter("triggerValue"); System.out.println("update()"); System.out.println("triggerVal = " + triggerVal); HttpServletResponse response = ServletActionContext.getResponse(); response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); Collection<String> targetValues = this.getTargetList(triggerVal); PrintWriter out = null; try { out = response.getWriter(); out.print("<select id='targetSelectionId' >"); for (String targetVal : targetValues) { out.println("<option>" + targetVal + "</option>"); } out.print("</select>"); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.flush(); out.close(); } } return null; } /** * This method is called by the 'list' attribute on the trigger * select control. * @return Returns the collection used to populate the "trigger' * select box. */ public Collection<String> getTriggerList() { Collection<String> keys = dataModel.keySet(); return keys; } /** * @return Returns the Collection used to populate the 'target' * select box, which is a function of the selected value in the * 'trigger' select box. */ private Collection<String> getTargetList(String trigger) { Collection<String> target = dataModel.get(trigger); if (target == null) { return Collections.emptyList(); } return target; } /** * This method is called by the 'trigger' select box, which has * the 'name' attribute value of 'triggerValue' * @param trigVal */ public void setTriggerValue(String trigVal) { System.out.println("setTriggerValue() = " + trigVal); //$NON-NLS-1$ if (trigVal == null || trigVal.length() == 0) return; this.triggerVal = trigVal; } /** * This is called by the s:url tag for the 'target' s:div tag, which * uses this value to append the initial target value to the url. * @return Returns the current 'trigger' value. */ public String getTriggerValue() { System.out.println("getTriggerValue() returns: " //$NON-NLS-1$ + this.triggerVal); return this.triggerVal; } } // end class 'AjaxDDS_DemoAction' And here is the jsp page (it is in 'jspx' format): <?xml version="1.0" encoding="UTF-8"?> <jsp:root version="2.0" xmlns:jsp="http://java.sun.com/JSP/Page" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:s="/struts-tags" xmlns="http://www.w3.org/1999/xhtml" xmlns:tiles="http://struts.apache.org/tags-tiles" > <jsp:output omit-xml-declaration="true" doctype-root-element="html" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" /> <jsp:scriptlet> String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); pageContext.setAttribute("BasePath",basePath); </jsp:scriptlet> <jsp:directive.page contentType="text/html" /> <head> <title>Dynamic Double Select with Ajax</title> <s:head theme="ajax" /> <meta name='Author' content='Ray Clough' /> <meta name='keywords' content='Greetings Earth - Struts_2 Demo'/> <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> <meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="cache-control" content="no-cache" /> </head> <script type="text/javascript"> // <![CDATA[ var basePath = "${BasePath}"; function updateTarget() { //alert('function: updateTarget()' ); var triggerId = document.getElementById("triggerId"); var triggerValue = triggerId.value; var triggerWidget = dojo.widget.byId("targetDivId"); //alert(triggerWidget); var target = document.getElementById("targetDivId"); var url = target.getAttribute("href"); var newUrl = basePath + "/greetings/AjaxDynDoubleSelect_update.action?triggerValue=" + triggerValue; triggerWidget.setUrl(newUrl); /* this publishes the topic, which is registered to the div tag, causing it to refresh itself. */ dojo.event.topic.publish("updateTargetTopic", "triggerValue:" + triggerValue); /* this is an alternative to publish (above) */ //triggerWidget.refresh(); } // ]]> </script> <body> <form id="ddsFormId" > <table style="width:100%;"> <tr> <td style="width:25%; text-align:right;" >Trigger: </td> <td style="width:*%; text-align:left;" > <s:select id="triggerId" name="triggerValue" list="triggerList" onchange="javascript: updateTarget(); " /> </td> </tr> <tr> <td style="width:25%; text-align:right;" >Target: </td> <td style="width:*; text-align:left;" > <s:url id="target_url" value="${BasePath}/greetings/AjaxDynDoubleSelect_update.action" > <s:param name="triggerValue" value="%{triggerValue}" /> </s:url> <s:div id="targetDivId" theme="ajax" listenTopics="updateTargetTopic" href="%{target_url}" > FROGS </s:div> </td> </tr> </table> </form> </body> </jsp:root> -- View this message in context: http://www.nabble.com/S2---Ajax-Dynamic-Double-Select-Demo---complete-code-included-tf3837616.html#a10865642 Sent from the Struts - User mailing list archive at Nabble.com. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]