Adding Ajax to Existing Applications Part 2

Now that you know how to grab XML via Ajax and display it in an HTML table, let's see if we can create a page with a drop-down menu that allows us to choose a catgory of book and display only that type of book in the table. The question is - how do we grab only the books we want? I see two possibilities:

  1. You could request all of the books. Then, in the processResults() function, you could detect which category of books was chosen and add only those books that fit the category to the HTML string.
  2. You could get the server involved and create a page that sends your request only the books that match the category.

The first option sounds pretty good - since we already possess the skills necessary to implemet the solution! The problem lies in the scale of the data. For our relatively small XML file, we could probably get away with looping through the nodes and picking off the matches. Think about what would happen at Amazon.com though. Imagine if Amazon.com sent each request all of the books it offers. Ajax is supposed to make web applications feel more responsive - not more sluggish.

That leaves the second option - we need to access a resource that serves up an XML document to our request containing matches to the chosen category. Quite frankly, this is the approach in the "real world." There are a few technologies that can do this for us. The three most popular are ASP and ASP.NET, PHP, and Java. I don't want to debate the merits of each technology as they all have strengths and weaknesses. However, using Java makes sense on a number of levels. First, Java is the language taught at NCC in the Internet Technology program. So, anything you learn here will help you if you plan to continue on in the program. Second, Netbeans is a Java IDE and so supports all of the Java technologies we need right out of the box. You won't need to install anything to get your applications to work. Finally, I'm the guy teaching the higher-level Java classes at NCC - so I have experience writing Java Servlets and Java Server Pages (JSP).

The good news is that Java code looks very much like JavaScript code so it shouldn't look all that foreign to you. You won't have to create any server side resources for this class. You will, however, be responsible for accessing server side resources. For now, don't worry about it. If you have questions regarding the server side stuff, ask away. I'd be happy to try to answer.

1. Create the HTML page first. Right click the Web Pages node in your AjaxBooks application and choose New > HTML... Name the file filterBooks and click Finish.

2. Add the following code between the <body> tags:

    <form>
<select name="category" onChange="sendReq('get',getURL(this.value))">
<option value="database">Database</option>
<option value="web">Web</option>
<option value="JavaScript">JavaScript</option>
<option value="graphics">Graphics</option>
<option value="programming">Programming</option>
<option value="HTML">HTML</option>
</select>

</form>
<div id="books">

</div>

Notice that we'll need to define the getURL() function at some point.

3. Add a reference to xhr.js in the <head>

<script type="text/javascript" src="xhr.js"></script>

4. Add the following <script> to the <head>

    <script type="text/javascript">
function processResults(){
if(req.readyState == 4){
if(req.status == 200){
var display = '<table border="1">';
display += '<tr><th>ISBN</th><th>Title</th><th>Category</th><th>Description</th><th>Price</th><th colspan="2">Author</th><th>Cover Pic</th></tr>';
var records = req.responseXML.documentElement.getElementsByTagName('record');
var len = records.length;
for(var i=0; i<len; i++){
display += '<tr>';
var data = records[i].childNodes;
var dLen = data.length;
for(var j=0; j<dLen; j++){
if(data[j].nodeName == '#text'){
continue
}
display += '<td>' + data[j].firstChild.nodeValue + '</td>';
}
display += '</tr>';
}
display += '</table>';
document.getElementById('books').innerHTML = display;
}
}
}

function getURL(val){
return 'filterBooks.jsp?category=' + val;
}
</script>

The processResults() function is identical to that in showBooks.html. That makes sense when you consider that the server side resource will do the matching for us. We just need to process whatever the server sends us.

The getURL() function is interesting in that it constructs a URL for us that includes a reference to which category is chosen. The data that comes after the "?" in the URL is called the query string. The query string consists of key/value pairs separated by ampersands. You use the query string when sending "get" requests. Normally, the query string is visible in the address bar of the browser. In Ajax applications though, the query string is usually invisible since the browser's address bar doesn't change. In our case, we are only sending one key - category - and one value - whatever value is held in the category drop-down. The server side resource - in this case the JSP named filterBooks - can expect to receive the data and act on it.

5. Right Click the Web Pages folder and choose New > JSP...

6. Name the file filterBooks and click Finish

7. Netbeans will create the file and open it in the editor. Replace any code in the file with the following code:

<%@page contentType="text/xml"%>
<%@page pageEncoding="UTF-8"%>
<%@page import="javax.xml.parsers.*,org.w3c.dom.*,java.io.*" %>
<%
//Grab category
String val = request.getParameter("category");
//Get reference to xml file
File booksFile = new File(application.getRealPath("/allBooks.xml"));
//Create DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//Create DocumentBuilder
DocumentBuilder db = null;
db = dbf.newDocumentBuilder();
//Parse xml to Document object
Document allBooks = null;
allBooks = db.parse(booksFile);
//All data stored in <record> elements
NodeList allRecords = allBooks.getElementsByTagName("record");
//Get number of records for loop
int len = allRecords.getLength();
//Create StringBuffer to hold xml for matching records
StringBuffer buffer = new StringBuffer(256);
buffer.append("<root>");
//Loop through records and grab those with matching category
for(int i=0; i<len; i++){
//need to access Category node
if(allRecords.item(i).getChildNodes().item(5).getTextContent().equals(val)){
//need to loop through childNodes here
buffer.append("<record>");
int cLen = allRecords.item(i).getChildNodes().getLength();
for(int j=0; j<cLen; j++){
if(allRecords.item(i).getChildNodes().item(j).getNodeName().equals("#text"))
continue;//don't need/want text nodes
//populate StringBuffer w/ matches
buffer.append("<");
buffer.append(allRecords.item(i).getChildNodes().item(j).getNodeName());
buffer.append(">");
buffer.append(allRecords.item(i).getChildNodes().item(j).getTextContent());
buffer.append("</");
buffer.append(allRecords.item(i).getChildNodes().item(j).getNodeName());
buffer.append(">");
}
buffer.append("</record>");

}
}
buffer.append("</root>");
//Write StringBuffer to response
out.print(buffer.toString());
%>

Without going into too much detail, at the very least you should note that Java code looks very much like JavaScript, doesn't it? The first line is, from the perspective of an Ajax application, the most important. It sets the content type of the response to XML. The rest of the code is relatively straightforward. After the encoding is set, the necessary class files are imported. That gives us access to the objects necessary to open the allBooks.xml file and parse it as an XML document.

Inside the scriptlet (the stuff between the <% and %> tags), the first line grabs the category value from the query string attached to the request and stores it in the variable named val. The next few lines open and parse the file and create a NodeList containing all the records. That Node List is then looped through. If the data stored in the Category node matches the category requested, the data is added to the StringBuffer holding the XML data. After all of the matching records are gathered, the StringBuffer is written out to the response.

8. Right click the filterBooks.html file and choose Run File. When you change the value of the drop down, the books displayed should change based on your request.