web     learntechnology.net
Ajax - Updating Many Page Items (with APT)
[ print format ]
ajax-sports.zip source code
ajax-sports.war

( Note: 11/14/07 If doing any new ajax development, I'd use ExtJS and/or JQuery )


Introduction

Take the scenario where you are supposed to provide a multiple select options box and allow the user to select several items. The rest of the form items on the page need to change dynamically based on the various multiple items selected. Without using any Ajax techniques, providing a nice user experience becomes difficult.

The simple solution is to provide an onblur event when you leave the select box which will submit your form, refresh the page, and update accordingly. This solution is very annoying for the user. If the server response is slow, the user could start to select other form items and suddenly the page refreshes while the user is selecting items or filling in data. Not good.

The other solution makes for a decent user experience but it's a PAIN to code - loading every possible item into javascript arrays and then using javascript calls to populate the other form elements as needed. This will end up working out fine from and end-user perspective, but coding all that javascript is just way too annoying in my opinion.

Using Ajax, however, the solution is much simpler. Each onchange event, as the user selects items from the multiple options list, will fire off your Ajax event and update the page accordingly. I admit that I know very little about how the whole Ajax XMLHTTPRequest stuff works. The good thing is using the AjaxParts Taglib from the Java Web Parts project, I don't have to. I let the AjaxParts tags take care of generating all the needed javascript and I just have to worry about coding a simple configuration file.

Hopefully, this simple lesson will show you how easy it is to do some quite powerful things with Ajax. I've already written one lesson using the AjaxParts tags that you can find here. That lesson shows a simple case where you choose one option from a select list and it repopulates another select list with new items. That lesson, however, shows a different technique where the actual html markup is generated in the Servlet that gets called.

This lesson shows an approach that I actually prefer to use above all the others. The concept is your Ajax event fires and hits a servlet. The servlet then gets the information that is needed and populates it in the request. The servlet then forwards to a JSP page. The JSP's purpose is to generate javascript which gets executed by the AjaxParts taglib. The nice thing about this approach is you can do a lot of different javascript things all within the JSP page. I also like the seperation of concerns - the servlet populates what the resulting JSP needs, and the JSP handles the presentation (well, presentation in this case is a bit of a stretch - but I still find it relatively clean to see the javascript in the JSP.) There are a few drawbacks to this Ajax process - but to me Ajax overall is not 'super clean' - after all we are still dealing with HTTP here, not a desktop application.)

What's in this example

This example isn't really based off anything super practical. It's a simple screen where the user can select multiple states. As the user selects states, the states he/she selected will appear next to the select list and two other select lists will be populated with sports teams that are found in the states the user selected.

Requirements

This application requires an application server that implements the Servlet 2.4 and JavaServer Pages 2.0 specifications. The examples should all work on Tomcat 5.x. Please do not e-mail about getting your application to run on a server other than Tomcat. The source code (and an Ant build file) is provided for all the lessons so you should be able to build a war from the source and run it on the application server of your choice.

JWP Jars
The two Java Web Parts jars needed for this application are:
  • javawebparts-core.jar
  • javawebparts-ajaxparts.jar

The jars are included in the source and war for this lesson or you can of course get the latest jars by downloading the latest release of Java Web Parts.

Configuring our web.xml

In our web.xml file the following is added to provide our Ajax functionality:


<!-- This parameter is needed to initialize AjaxTags.  It points to the -->
<!-- context-relative configuration file. -->
<context-param>
    <param-name>ajaxTagsConfig</param-name>
    <param-value>/WEB-INF/ajax-config.xml</param-value>
</context-param>

<!-- This listener runs on app start up and loads up our 
    List of states into application scope -->
<listener>
    <listener-class>net.learntechnology.ajax.sports.AppContextListener</listener-class>
</listener>

<!-- This servlet will be called when states are selected -->
<servlet>
    <servlet-name>StateChangedServlet</servlet-name>
    <servlet-class>net.learntechnology.ajax.sports.StateChangedServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>StateChangedServlet</servlet-name>
    <url-pattern>/ajax/stateChangedServlet</url-pattern>
</servlet-mapping>
index.jsp

I'm not going to paste the whole file here. I'm assuming you've downloaded the source code. I'll comment on the important parts.

The first thing we need to remember is to include our taglib reference at the top of our JSP:


<%@ taglib prefix="ajax" uri="javawebparts/ajaxparts/taglib" %>

We also need to make sure we include an Ajax enabled tag somewhere at the end of our JSP. This tag will be responsible for generating javascript based off our form elements. The best place for this tag is right before the closing body tag (This way if you use a decorating framework lke Sitemesh the resulting generated code will be sure to be carried over to the decorated content).


<ajax:enable />
</body>

The states select options:


<select name="states" multiple="true" size="5">
    <c:forEach var="state" items="${states}">
        <option value="${state.id}"><c:out value="${state.name}" /></option>
    </c:forEach>
</select><ajax:event ajaxRef="sportsForm/statesChanged"/>

The IMPORTANT part above is the use of the ajax:event tag. How the ajax:event is implemented is based on the ajaxRef attribute which looks in our ajax-config.xml file for its behavior. The ajax-config.xml is shown in the next major section.

You'll also notice in the jsp several divs such as...
<div style="vertical-align:top;" id="statesChosen">

These divs with an id will targeted by our JSP's javascript that will change the contents of them.

ajax-config.xml

<ajaxConfig>
    <group ajaxRef="sportsForm">
        <element ajaxRef="statesChanged">
            <event type="onchange">
                <requestHandler type="std:QueryString" target="/ajax/stateChangedServlet">
                    <parameter>statesSelected=states</parameter>
                </requestHandler>
                <responseHandler type="std:CodeExecuter">
                    <parameter/>
                </responseHandler>
            </event>
        </element>
    </group>
</ajaxConfig>

The docs here do a good job explaining all the parts of this file. Notice the use of the group and element ajaxRef values. You'll see in our ajax:event tag the use of ajaxRef="sportsForm/statesChanged" which drills down to the event we are concerned with.

How many groups you want to have in your ajax-config.xml file is up to you. I typically like to have one per JSP or form, however if you have an event that is going to be fired from a lot of different pages, you might want to organize things differently.

I'm not going over the different types of requestHandlers (see docs) but notice the parameter <parameter>statesSelected=states</parameter>. 'statesSelected' is the actual Request parameter that will be passed to our servlet, and based off this mapping, you can tell it's going to use the 'states' parameter off the form (states is the name of our select box).

The responseHandler section you'll notice is different from the previous Ajax lesson. This one uses CodeExecuter, which means it will execute the javascript that is returned in the response (in this case our JSP).

StateChangedServlet
Out StateChangedServlet's doPost method looks like:

public void doPost(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException, IOException {
        log.debug("In doPost of StateChangedServlet");
        String[] statesSelected = request.getParameterValues("statesSelected");
        List teams = Service.getTeamsForStates(statesSelected);
        request.setAttribute("teams",Service.getTeamsForStates(statesSelected));
        List statesChosen = Service.getStatesByIds(statesSelected);
        request.setAttribute("statesChosen", statesChosen);
        request.getRequestDispatcher("/ajaxJavascriptResults.jsp").forward(request, response);
    }

Notice the Request parameter "statesSelected" which matches up with what we defined in our ajax-config as a parameter for this request. Based off that param we then populate the statesChosen and the teams that go with those states and put them in the Request. We end by forwarding to our JSP which will get executed as javascript.

ajaxJavascriptResults.jsp
The below should be pretty self-explanatory. It's a bit ugly since we're using JSTL to generate the javascript, but it's going to be ugly somewhere - in my opinion, might as well be in a JSP.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
s = ""
<c:forEach items='${statesChosen}' var='state' varStatus='status'>
    s += "<c:out value='${state.name}'/>";
   <c:if test='${!status.last}'>
        s += ", ";
   </c:if>
</c:forEach>
document.getElementById("statesChosen").innerHTML = s;

teamOptions = "";
<c:forEach items='${teams}' var='team' varStatus='tStatus'>
    teamOptions += "<option value=\"<c:out value='${team.id}'/>\"><c:out value='${team.name}'/></option>";
</c:forEach>

s = "<select name='favoriteTeam'>"+teamOptions+"</select>";
document.getElementById("favoriteTeam").innerHTML = s;

s = "<select name='closestToYou'>"+teamOptions+"</select>";
document.getElementById("closestToYou").innerHTML = s;    
Conclusion

Run the war file and you'll see the application in action. The latest release of AjaxParts also allows for pre and post javascript event calls which I really like. Say you need to run some local javascript before the Ajax event fires, it's very easy to do by defining the event you want to run first as preProc attribute in the ajax-config. Same thing with a post event. The part that I don't like about this Ajax process is that it seems like you'll have to maintain the creation of certain dropdown lists in two places. Often times the initial JSP will create the select list in a div and later on an Ajax event fires and has to write out the same select options to the same div. Maybe there is a way around having to maintain the same type of code in two places that I just haven't thought of.

Code and Lesson - Rick Reumann (special thanks to Wendy Smoak for her revisions.)