6.2. Using the Rico ToolkitTo experiment with Rico, download the latest version from http://openrico.org/rico/downloads.page. The Rico Toolkit depends on the Prototype library, so start by importing the prototype.js and rico.js files into your HTML page: <script language="JavaScript" src="/books/4/163/1/html/2/scripts/prototype.js"></script> <script language="JavaScript" src="/books/4/163/1/html/2/scripts/rico.js"></script> Then you need to register a request handler: ajaxEngine.registerRequest('zipRequestHandle', 'rico'); The first parameter, zipRequestHandle, is the handle we'll use to make the Ajax request. The second parameter is the string rico, which is registered with the ajaxEngine. That is the relative URL used when the zipRequestHandle is invoked. Therefore, we need to map the servlet to this URL in web.xml: <servlet> <servlet-name>RicoZipCodesServlet</servlet-name> <servlet-class> com.oreilly.ajax.servlet.RicoZipCodesServlet </servlet-class> <load-on-startup>5</load-on-startup> </servlet> <servlet-mapping> <servlet-name>RicoZipCodesServlet</servlet-name> <url-pattern>/rico</url-pattern> </servlet-mapping> Everything is now set up. Back in the HTML page, the zip code text field uses the Ajax engine to send the request to the server, using the zipRequestHandle: <td>Zip Code:</td> <td align="left" colspan="2"> <input type="text" name="zipcode" onblur="ajaxEngine.sendRequest('zipRequestHandle', 'zip='+escape(this.value));"> </td> The onblur JavaScript event is set to call ajaxEngine.sendRequest( ), sending the zipRequestHandle that we initialized earlier and passing in the value of the input field, which should be a zip code. When the user enters a zip code and presses the Tab key to move the cursor to the next field, the onblur event triggers the Rico ajaxEngine to send a request to the specified URL. The server should then respond with an XML-formatted document that looks like this: <ajax-response> <response type="element" >some content for the city</response> <response type="element" >some content for the state</response> </ajax-response> The root of the XML response must be <ajax-response>. Each element within the response must be wrapped in a <response> tag, with the id and type attributes defined. When an <ajax-response> document comes back from the server, Rico matches the id attributes in the document with the fields in the HTML form and populates the form accordingly. In this document, we have two IDs: cityDiv and stateDiv. The data from these XML elements is used to populate the HTML tags with the cityDiv and stateDiv IDs in our HTML document: <tr> <td>City:</td> <td align="left"> <div > <input type="text" name="inputcity"> </div> </td> </tr> <tr> <td>State:</td> <td align="left" colspan="2"> <div > <input type="text" size="2" name="inputstate"> </div> </td> </tr> So, if an HTML element has an id that matches an id in the response, Rico updates that HTML element with the content from the XML document. What goes in the <response> elements? It would be simplest if we could just insert the city and state names, but Rico replaces everything inside the tags with the cityDiv and stateDiv IDs with the content from the XML document. In our HTML, the cityDiv and stateDiv IDs are assigned to <div> elements. Therefore, we need to supply new <input> tags to replace the contents of the divs in the HTML document.
So, the buildRicoXML( ) method needs to generate the content for a div that includes the HTML inputs. Also, we want to add a div to hold a message in case the zip code is not in the database: public static String buildRicoXML(HashMap map,String element, String message) { StringBuffer ricoXML = new StringBuffer("<ajax-response>"); String key = ""; String value = ""; // loop through all the map entries Iterator it = map.entrySet().iterator( ); while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); ricoXML.append("\r\n <response type=\"element\" id=\"" + key + "\">" + "<input type=\"text\" id=\"inner"+key+"\" name=\"inner"+key+"\" value=\""+value+"\" \><\response>"); } ricoXML.append("\r\n <response type=\"element\" id=\"message\" name=\"message\">"+message+"<\response>"); ricoXML.append("\r\n</ajax-response>"); return ricoXML.toString( ); } Now the zip code lookup application is ready. You'll first see a browser with three empty fields (Figure 6-1). Figure 6-1. Rico zip code lookupIf a user enters a zip code that is not in the database, the message div instructs that user to manually enter the city and state (Figure 6-2). Figure 6-2. Unsuccessful zip code lookup with messageHere's the code that creates the Rico response for the unsuccessful zip code lookup and message: <ajax-response> <response type="element" > <input type="text" name="innerstate" value=" " /> </response> <response type="element" > <input type="text" name="innercity" value=" " /> </response> <response type="element" name="message"> Zip code: 99999 is not in the database. Please enter your City and State </response> </ajax-response> When the user enters the city and state, you can capture that information, verify it, and add it to your zip code database. That goes beyond what we'll demonstrate here, but it would be a useful endeavor. What if the zip code is in the database? In that case, we don't really need to provide input fields; it may be cleaner to just display the city and state. To accomplish this, we could use a different method that doesn't fill the div with an input field but instead simply returns the city and state wrapped in the Rico response: public static String buildRicoXML(HashMap map, String message) { StringBuffer ricoXML = new StringBuffer("<ajax-response>"); String key = ""; String value = ""; // loop through all the map entries Iterator it = map.entrySet().iterator( ); while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); ricoXML.append("\r\n <response type=\"element\" id=\"" + key + "\">" + value + "<\response>"); } ricoXML.append("\r\n <response type=\"element\" id=\"message\" name=\"message\">"+message+"<\response>"); ricoXML.append("\r\n<\ajax-response>"); return ricoXML.toString( ); } Now, the XML we're returning looks like this: <ajax-response> <response type="element" >CA</response> <response type="element" >FRESNO</response> <response type="element" name="message"></response> </ajax-response> That results in the div containing only the bare text with no input fields for the user. The resulting page is shown in Figure 6-3. Figure 6-3. Successful zip code lookup with RicoRico requires a bit more setup, but it's worth it because you don't have to write a callback function: Rico automatically populates the fields you need with the data that comes back. This approach works fine if you want to fill document elements with values coming back from an HTTPRequest, but what if you need to look at the data or modify it? That's where Rico's Object Response Type comes into play. 6.2.1. Using Rico's Object Response TypeThe Object Response Type allows the client JavaScript code to pull each element out of the XML response. It can then modify it or put it into the document as is. To use the Object Response Type, we must register an object with the Rico ajaxEngine: ajaxEngine.registerAjaxObject('locationUpdater', cityStateUpdater); Now we need to create a cityStateUpdater object that has the method ajaxUpdate( ), as shown in Example 6-2. Example 6-2. Creating an object for registerAjaxObject( )
We must then ensure that the XML response coming back from the server has the attribute type="object" and an id that matches the object set by registerAjaxObject( ). In this case, the id must be locationUpdater, and the data must be attributes in XML format. The XML response should look like this: <ajax-response> <response type="object" > <location state="OH" city="NEWTON FALLS" /> </response> </ajax-response> Here is the servlet code that produces the response: public static String buildRicoObjectXML(HashMap map, String message) { StringBuffer ricoXML = new StringBuffer("<ajax-response>/r/n <response type=\"object\" id=\"locationUpdater\"><location "); String key = ""; String value = ""; // loop through all the map entries Iterator it = map.entrySet().iterator( ); while (it.hasNext( )) { Map.Entry e = (Map.Entry) it.next( ); value = (String) e.getValue( ); key = (String) e.getKey( ); ricoXML.append(key+"=\""+ value +"\" " ); } ricoXML.append("/></response>\r\n</ajax-response>"); return ricoXML.toString( ); } If you missed how the data was inserted in the form, look back at the setFields( ) function in Example 6-2. That function merely gets an element from the DOM and puts the value from the XML response into that element. In this method, you can extract a value from the XML response and modify it or implement some logic based on the value. |