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]

Reply via email to