Sending the Response Back to the Client
After doPost adds an item to the list, it generates a response containing the updated list and sends it back to the client. It does this by iterating through the list and doing the following for each item in the list:
Generates a div tag.
Adds onmouseover and onmouseout attributes to the div tag so that styles are applied to an item in the list when the user passes the mouse over it.
Adds an onclick attribute to the div tag that is set to the removeItem JavaScript function, which takes the index of the selected item.
Here is the code from the doPost method that generates the response:
java.util.Iterator it = list.iterator(); int counter=0; PrintWriter writer = response.getWriter(); writer.write("<ol>"); while(it.hasNext()) { writer.write("<li><div class=\"plain\" onmouseover=\"this.className ='over';\" onmouseout=\"this.className ='plain';\" onclick=\"removeItem('" + counter++ + "')\">" + it.next() + "</div></li>"); } writer.write("</ol>");
The next section explains how the XMLHttpRequest object processes the response that the server sends.
Updating the HTML DOM
Recall from section Creating and Configuring the XMLHttpRequest Object that the initRequest function includes a callback function, named processRequest, and that this function is called when the ready state of the XMLHttpRequest object changes, as shown in the following code:
req.onreadystatechange = processRequest; function processRequest () { if (req.readyState == 4) { if (req.status == 200) { list.innerHTML = req.responseText; } } }
The processRequest function first checks if the ready state is 4, which means that the XMLHttpRequest call is complete. If the call is complete, the function checks if the HTTP status code of the request is 200, signifying a successful HTTP interaction. If the status is 200, then processRequest sets the innerHTML of the list element in the DOM to the response sent by the server. The innerHTML property of a particular DOM element is a String representation of the element's children. By setting the list element's innerHTML property to the response, you are replacing the list div tag shown in sectionGenerating the Client Event with the response. For example, if the list object has two items in it, the resulting page would look something like the following:
<div id="listForm" class="listContainer"> <form name="autofillform" onSubmit="submitData(); return false;"> <input id="entryField" type="text" size="20" value="Enter New Value"> <input type="button" onclick="submitData(); return false;" value="Add to List"> </form> <ol> <li> <div class=\"plain\" onmouseover=\"this.className ='over';\" onmouseout=\"this.className='plain';\" onclick=\"removeItem('"0"')\">" + it.next() + " </div> </li> <li> <div class=\"plain\" onmouseover=\"this.className ='over';\" onmouseout=\"this.className='plain';\" onclick=\"removeItem('"1"')\">" + it.next() + " </div> </li> </ol> </div>
As the preceding markup shows, clicking one of the items in the list generates an onclick event, which causes the removeItem function to be called with the index of the item. Similarly to the submitData function, the removeItem function creates a URL and passes it to the initRequest function. The URL consists of the list URL pattern, the command request parameter with a value of remove, and the index request parameter set to the index of the item the user clicked, as shown in the following code:
function removeItem(index) { var url = "list?command=remove&index=" + index; initRequest(url); }
Once the initRequest function is invoked, the request and response mechanism proceeds as it does when adding an item to the list, except that doPost executes the following code to remove the item from the list:
if ("remove".equals(command)) { String indexString = request.getParameter("index"); if (indexString != null) { int index = Integer.valueOf(indexString); list.remove(index); } }
The .over and .plain styles used by onmouseover and onmouseout are included in index.jsp and indicate to the user that clicking on an item in the list will remove it. When the user passes the mouse over an item, the following style is applied to it:
.over { color: white; height:25; font-size:18px; font-weight: bold; font-family: Arial; background: blue; cursor: pointer; }
As you can see, at the onmouseover event, the background changes to blue, the text changes to white, and the cursor turns into a pointer, indicating that clicking on the item will have an effect.
Contributions of Ajax Toolkits
As you've seen from the shopping list example, using Ajax requires some JavaScript expertise. When running Ajax applications on different browsers, you'll also notice that the browsers provide inconsistent support of the Ajax functionality. New Ajax frameworks have emerged to help alleviate these problems. Once you learn to master one of these frameworks, you'll find it helps a lot with developing Ajax-aware applications and resolving some common Ajax pitfalls.
One of the frameworks that makes it easier to use Ajax is the Dojo open-source JavaScript toolkit. First of all, it frees developers from writing more common JavaScript functions by providing pluggable JavaScript libraries geared toward particular tasks, such as event-handling. It also overcomes some of the inconsistent browser support of Ajax as well as the memory leaks that plague JavaScript. Perhaps most importantly to the Ajax developer, it supports the XMLHttpRequest mechanism so that you don't need to deal with it yourself in your JavaScript. It also offers a library of Ajax-enabled widgets that you can drop into your applications.
The Dojo io package makes it easier to exchange data with the server by encapsulating the XMLHttpRequest mechanism. When using the dojo.io.bind method, you don't need to implement the JavaScript functions that create, initialize, and configure the XMLHttpRequest object, and you are able to hide the calls that the XMLHttpRequest makes to the server.
The following code shows how you could use the dojo.io.bind method to eliminate the code discussed in Creating and Configuring the XMLHttpRequest Object and Sending an Ajax Request.
var list; function submitData() { var entryField = document.getElementById("entryField"); var submitUrl = "list?command=add&entryField=" + entryField.value; sendRequest(submitUrl); } function removeItem(index) { var removeUrl = "list?command=remove&index=" + index; sendRequest(removeUrl); } function sendRequest(requestUrl) { dojo.io.bind({ url: requestUrl, method: "post", mimetype: "text/plain", load: function(type, data) { list = document.getElementById("list"); list.innerHTML = data; } }); }
The sendRequest function in the preceding code passes this set of arguments to the dojo.io.bind method:
url: This is the URL that the XMLHttpRequest object passes to the server-side object, which will use it to create an XML document with the component IDs and values. In this case, the submitData and removeItem functions call the sendRequest function with the URLs that contain the appropriate request parameters so that the servlet will take the proper action, as described in Processing the Ajax Request.
method: This indicates the HTTP method to use when the function is called. In this case, we want to use POST to post data to the server.
mimetype: Indicates the MIME type of the content to be passed from the servlet.
load: Represents the callback function that is invoked after the XML data is received. The type argument is the type of the function, which is always load. The data argument represents the data that is passed to the function by the server.
Encapsulating Ajax Functionality in Web Components
As this chapter has shown so far, Ajax is a powerful technology but can be difficult to implement. Various toolkits help make development with Ajax a little easier. One issue they don't address is how to add Ajax to a web application in a robust and scalable way so that the Ajax functionality blends well with the sophisticated features offered by many web application development platforms.
One such platform is the Java EE platform. Using Java EE platform tools and technologies to build Ajax-enabled applications gives your application access to the entire Java EE platform stack, including new and updated web services and database access technologies. In the stack's web tier, you get servlets, JavaServer Pages (JSP) technology, and Java Standard Tag Library (JSTL). You also get JavaServer Faces technology 1.2, a framework for building rich user interfaces (UIs) for web applications. It offers a sophisticated, extensible component model for handling events, converting and validating data, and managing component state.
In addition to giving you the extra server-side functionality you need for Ajax, JavaServer Faces technology makes it easy for you to add Ajax to your application. Instead of embedding the JavaScript technology directly in the page, you can encapsulate it inside of a JavaServer Faces component, thereby leveraging all the benefits that the JavaServer Faces component model gives you.
If are using JSP technology without JavaServer Faces technology, you can also encapsulate Ajax functionality using custom tags. Coupled with the rest of the Java EE platform stack, the JSP and JavaServer Faces technologies give you everything you need to complete the server-side picture of your Ajax-enabled web application.
You have a lot of options regarding how you encapsulate Ajax functionality inside web components. In fact, the Java Blueprints project offers recommendations for enriching a web component with Ajax functionality and also includes a library of Ajax-enabled JavaServer Faces components. The article Creating an Ajax-Enabled Application, a Component Approach describes a couple of techniques for including Ajax in a JavaServer Faces component.
These techniques offer a better way to Ajax-enable a web application. Still, they require component developers to modify their components to add Ajax functionality to them and to know JavaScript well enough to do so.
This is where Project jMaki and Project Dynamic Faces come in. They allow you to reap the benefits of encapsulating Ajax functionality in web components but can help eliminate the need to modify components or application code to add Ajax to your web applications.