Previous Page
Next Page

Hack 53. Use Rico to Update Several Elements with One Ajax Response

Use the Rico library to automatically update several weather-related web page elements with one Ajax request.

This hack uses Rico to make a single Ajax request that can update several weather-related page elements at once without refreshing the page. It grabs the weather information for four U.S. cities from the Weather.com web service.

Using Rico requires the hack to import the rico.js and prototype.js libraries upon which Rico is built (see "Use Prototype's Ajax Tools with Your Application" [Hack #50]). The custom code for the hack resides in multiple.js. Here are the script tags for the HTML page:

<script src="js/prototype.js" type="text/javascript"></script>
<script src="js/rico.js" type="text/javascript"></script>
<script src="js/multiple.js" type="text/javascript"></script>

The hack shows the weather information in several regions of the page.

The weather icons derive from the Weather.com SDK [Hack #31].


When the user clicks the Update Weather! button, the code makes an Ajax connection using the Rico.AjaxEngine object, which is included in the Rico library. Figure 6-5 shows what the page looks like in Firefox 1.5.

Figure 6-5. Updating weather information


Our own JavaScript in multiple.js uses only a few lines of AjaxEngine code to implement an XMLHttpRequest object and update the content of four elements on the page. You do have to craft the format of the server's response to Rico, but we'll get to that code in a moment.

The code that sets up the Update Weather! button's behavior is found in our JavaScript file for this page:

window.onload=function(  ){
    if($("w_update")){
        $("w_update").onclick=function(  ){
            updateWeather(  );
        }
    }
};

function updateWeather(  ){
    ajaxEngine.registerRequest("multiple", "/parkerriver/s/wdisp");
    ajaxEngine.registerAjaxElement("boston");
    ajaxEngine.registerAjaxElement("boulder");
    ajaxEngine.registerAjaxElement("portland");
    ajaxEngine.registerAjaxElement("seattle");
    ajaxEngine.sendRequest("multiple","");
}

ajaxEngine is a variable that refers to the Rico.AjaxEngine object. This variable is automatically available from rico.js and does not have to be instantiated by the hack's code. The Rico.AjaxEngine object is the object that does all the request-related work for the hack and uses XMLHttpRequest beneath the surface. To implement the multiple-update task of this hack, the code must call this object's registerRequest( ) method, specifying the name you are giving the URL to the server component (here, "multiple"), as well as the actual URL.

Specifying the relative URL (i.e., without a protocol or host) seems to work fine for this method.


The engine also has to know about the page elements or nodes that it will update. Thus, the code calls registerAjaxElement( ) with the id of each span element that is updated from the server. Finally, the code calls sendRequest( ) with the registered name of the URL and any parameters (this code does not include any parameters). Here is the part of the web page that this request updates:

<div id="east" style="float: left">
<span class="cityhead">Boston, MA</span><br />
<span id="boston">
</span>
<br /><br />
<span class="cityhead" >Portland, OR</span><br />
<span id="portland">
</span>
</div>
<div style="float: left">&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div id="west" style="float: left">
<span class="cityhead">Boulder, CO</span><br />
<span id="boulder">
</span>
<br /><br />
<span class="cityhead">Seattle, WA</span><br />
<span  id="seattle">
</span>
</div>

The Rico.AjaxEngine object then updates the entire contents of each of the highlighted spans.

Ajax Convention

Rico uses a response convention to implement this task. With this hack, a typical response (formatted for printing in this book) looks like:

<?xml version="1.0" encoding="UTF-8"?>
<ajax-response>
<response type="element" id="seattle">
<img src="/parkerriver/ajaxhacks/img/11.png"/> 
<span id="rng_seattle" class="therm">high :: 48; low :: 44</span>
<span id="dt0" style="visibility: hidden">1/4/06 6:08 AM PST</span>
</response>
<response type="element" id="portland">
<img src="/parkerriver/ajaxhacks/img/11.png"/> 
<span id="rng_portland" class="therm">high :: 48; low :: 42</span>
<span id="dt1" style="visibility: hidden">1/4/06 5:04 AM PST</span>
</response>
<response type="element" id="boston">
<img src="/parkerriver/ajaxhacks/img/28.png"/> 
<span id="rng_boston" class="therm">high :: 34; low :: 31</span>
<span id="dt2" style="visibility: hidden">1/4/06 9:06 AM EST</span>
</response>
<response type="element" id="boulder">
<img src="/parkerriver/ajaxhacks/img/30.png"/> 
<span id="rng_boulder" class="therm">high :: 50; low :: 28</span>
<span id="dt3" style="visibility: hidden">1/4/06 7:08 AM MST</span>
</response>
</ajax-response>

The response is in XML format. It must have a root element of ajax-response, which itself can have any number of response child elements. This is how Rico manages to do multiple updates and more with one request.

Make sure that the response has a Content-Type of application/xml or text/xml.


The response element looks like:

<response type="element" id="seattle">

The id references the id that we "registered" with the Rico.AjaxEngine object. This is how Rico knows where all the returned HTML goes. Pretty straightforward, eh?

On the Server End

This hack has to initiate a certain amount of heavy lifting on the server end, in terms of fetching the weather data from Weather.com and then sending the formatted XML back to our Ajax application. Because XMLHttpRequest cannot connect with a different host from the host from which the user downloads the web page, the hack uses a server intermediary to talk to Weather.com and send the XML back to the web page. We use a Java servlet called WeatherManager.java and the JDOM API (see http://www.jdom.org) to make an HTTP connection with Weather.com. Weather.com returns weather data for the four cities specified in the response elements in XML format (see "Display a Weather.com XML Data Feed" [Hack #31]).

A Java servlet called WeatherDisplay.java then accesses an array of four HashMap objects from WeatherManager, each containing the latest available high and low temperatures, a date/time string, as well as associated image icons for the U.S. cities. With this information in hand, the servlet builds the required ajax-response element using a buffer of character data. The servlet then sends the response using a Content-Type of text/xml. Here's the servlet code; AjaxUtil is our own utility class:

public class WeatherDisplay extends HttpServlet {
    protected void doGet(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse) throws 
                         ServletException, IOException {
        StringBuffer buf = new StringBuffer("<ajax-response>");
        try {
            Map[] map = WeatherManager.getAllWeather(  );
            for(int i = 0; i < map.length; i++) {
                buf.append(getRicoResponse(map[i],i));
            }
        } catch (JDOMException e) {
            throw new ServletException(e);
        }
        buf.append("</ajax-response>");
        AjaxUtil.sendXML(httpServletResponse,buf.toString(  ));
    }

    protected void doPost(HttpServletRequest httpServletRequest, 
                          HttpServletResponse httpServletResponse) throws 
                          ServletException, IOException {
        doGet(httpServletRequest, httpServletResponse);
    }

    private String getRicoResponse(Map map,int counter) {
        StringBuffer buf = new StringBuffer(
                "<response type=\\"element\\" id=\\"");
        buf.append((String)map.get("id")).append("\\">\\n");
        buf.append("<img src=\\"/parkerriver/ajaxhacks/img/").
                append((String)map.get("img"));
        buf.append(".png\\"/>");
        buf.append(" <span id=\\"rng_").append((String)map.get("id")).
                append("\\" class=\\"therm\\">high :: ");
        buf.append((String)map.get("hi")).append("; low :: ").
                append((String)map.get("low"));
        buf.append("</span>\\n");
        buf.append("<span id=\\"dt").append(counter).
                append("\\" style=\\"visibility: hidden\\">").
                append((String)map.get("date"));
        buf.append("</span>\\n</response>");
        return buf.toString(  );
    }
}

Hacking the Hack

The response XML also includes the date and time associated with the weather data:

<span id="dt2" style="visibility: hidden">1/4/06 9:06 AM EST</span>

This data can be used in another iteration of the application to display more information to the user or to reduce server hits by updating the weather only if the current data display is a specified number of hours old.


Previous Page
Next Page