This document provides an introduction to Ajax and demonstrates some of the features in the NetBeans IDE that allow you to program faster and more efficiently when working with Ajax-related technologies. While learning about the low-level functionality of Ajax, you build a simple application that employs auto-completion in a text field. Content here has been adapted from Greg Murray's article and sample application from Using Ajax with Java Technology.
Ajax stands for Asynchronous JavaScript and XML. In essence, Ajax is an efficient way for a web application to handle user interactions with a web page - a way that reduces the need to do a page refresh or full page reload for every user interaction. This enables rich behavior (similar to that of a desktop application or plugin-based web application) using a browser. Ajax interactions are handled asynchronously in the background. As this happens, a user can continue working with the page. Ajax interactions are initiated by JavaScript code. When the Ajax interaction is complete, JavaScript updates the HTML source of the page. The changes are made immediately without requiring a page refresh. Ajax interactions can be used to do things such as validate form entries (while the user is entering them) using server-side logic, retrieve detailed data from the server, dynamically update data on a page, and submit partial forms from the page.
Contents
- Overview of the Application
- Programming the Client-Side: Part 1
- Programming the Server-Side
- Programming the Client-Side: Part 2
- Running the Application
- Conclusion
- See Also
To complete this tutorial, you need the following software and resources.
Software or Resource | Version Required |
---|---|
NetBeans IDE, Java bundle | 6.7, 6.8 or 6.9 |
Java Development Kit (JDK) | 6 |
GlassFish server or Tomcat server | v3 or Open Source Edition 3.0.1 version 6.x |
Notes:
- The Web and Java EE installation enables you to optionally install the GlassFish server 3.0.1 and the Apache Tomcat servlet container 6.0.x.
- This tutorial assumes that you have a working knowledge of the various technologies it employs (i.e., HTML, CSS, JavaScript, Java and JSP). It attempts to provide an overview of the functionality provided by the code, but does not explain how the code works on a line-to-line basis.
- If you need to compare your project with a working solution, you can download the sample application.
Overview of the Application
Imagine a web page in which a user can search for information about musical composers. The page includes a field where the user can enter the name of the composer. In the example application, the entry field has an auto-complete feature. In other words, the user can type in part of the composer name, and the web application attempts to complete the name by listing all composers whose first or last name begins with the characters entered. The auto-complete feature saves the user from having to remember the complete name of the composer and can provide a more intuitive and direct path to the sought-after information.
![Sample application displayed in browser Sample application displayed in browser](http://netbeans.org/images_www/articles/70/web/ajax-intro/sample-app.png)
Implementing auto-completion in a search field is something that can be performed using Ajax. Ajax works by employing an XMLHttpRequest
object to pass requests and responses asynchronously between the client and server. The following diagram illustrates the process flow of the communication that takes place between the client and server.
![Ajax process flow diagram Ajax process flow diagram](http://netbeans.org/images_www/articles/70/web/ajax-intro/ajax-process-flow.png)
The process flow of the diagram can be described by the following steps:
- The user triggers an event, for example by releasing a key when typing in a name. This results in a JavaScript call to a function that initializes an
XMLHttpRequest
object. - The
XMLHttpRequest
object is configured with a request parameter that includes the ID of the component that triggered the event, and any value that the user entered. TheXMLHttpRequest
object then makes an asynchronous request to the web server. - On the web server, an object such as a servlet or listener handles the request. Data is retrieved from the data store, and a response is prepared containing the data in the form of an XML document.
- Finally, the
XMLHttpRequest
object receives the XML data using a callback function, processes it, and updates the HTML DOM (Document Object Model) to display the page containing the new data.
This tutorial demonstrates how to construct the auto-complete scenario by following the process flow indicated in the above diagram. You first create the client-side files for the presentation and functionality needed to generate the XMLHttpRequest
object. Then, you set up the server-side by creating the data store and business logic using Java-based technology. Finally, you return to the client-side and implement callback()
, and other JavaScript functionality to update the HTML DOM.
Programming the Client-Side: Part 1
Begin by creating a new web application project in the IDE. The IDE contains built-in templates for numerous project types.
- Choose File > New Project. Under Categories, select Web. Under Projects, select Web Application then click Next.
- In the Name and Location panel, enter
MyAjaxApp
for Project Name. The Project Location field enables you to specify the location of the project on your computer. Leave other options at their defaults and click Next.
- In the Server and Settings panel, select the server you want to deploy your application to. Only servers that are registered with the IDE are listed.
- Accept other default settings and click Finish. The project is generated in your file system and opens in the IDE.
When Java-based web projects are created, an Ant build script is automatically generated which enables you to compile the project so that it can be immediately deployed and run on a server that has been registered in the IDE.
A default index.jsp
entry page is generated and opens in the IDE's Source Editor. Also, your project appears in the Projects window.
![Projects window displays MyAjaxApp project Projects window containing newly created project](http://netbeans.org/images_www/articles/70/web/ajax-intro/java-proj-win.png)
Before beginning to code, quickly try running the application to ensure that configuration between the IDE, your server, and browser is set up properly.
-
In the Projects window, right-click the project node and choose Run.
The application is compiled, the application server starts and the application is deployed to it and run. The IDE opens your default browser and displays the default Hello World message that is included in
index.jsp
.
Using the HTML Editor
![Palette displaying HTML elements Palette displaying HTML elements](http://netbeans.org/images_www/articles/70/web/ajax-intro/palette.png)
Now that you are certain your environment is set up correctly, begin by transforming your index page into the auto-completion interface that will be viewed by users.
One of the advantages of using an IDE is that the editor you work in often provides you with code completion which, if you learn to apply it when you code, can rapidly increase your productivity. The IDE's Source Editor generally adapts to the technology you are using, so if you are working in an HTML page, pressing the code completion key combination (Ctrl-Space) will produce suggestions for HTML tags and attributes. As will later be shown, the same applies for other technologies, such as CSS and JavaScript.
A second feature you can make use of is the IDE's Palette. The Palette provides easy-to-use templates for elements that are commonly applied in the technology you are coding in. You simply click on an item, and drag it to a location in the file open in the Source Editor.
You can view large icons (as displayed here) by right-clicking in the Palette and choosing Show Big Icons.
- Replace the content for the
<title>
tags to read:Auto-Completion using AJAX
. Also, replace the<h2>
tags with<h1>
tags, and type in the same text as you did<title>
tags. The index page requires no server-side scripting code, so you can safely delete any remnants that were created by default. The index page should now appear as shown below.<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Auto-Completion using AJAX</title> </head> <body> <h1>Auto-Completion using AJAX</h1> </body> </html>
- Add some explanatory text to describe the purpose of the text field. You can copy and paste in the following text at a point just beneath the
<h1>
tags:<p>This example shows how you can do real time auto-completion using Asynchronous JavaScript and XML (Ajax) interactions.</p> <p>In the form below enter a name. Possible names that will be completed are displayed below the form. For example, try typing in "Bach," "Mozart," or "Stravinsky," then click on one of the selections to see composer details.</p>
- Add an HTML form to the page. You can do this by making use of the elements listed in the IDE's Palette. If the Palette is not open, choose Window > Palette from the main menu. Then, under HTML Forms, click on and drag a Form element into the page to a point beneath the
<p>
tags that you just added. The Insert Form dialog box opens. Specify the following:
- Action: autocomplete
- Method: GET
- Name: autofillform
Click OK. The HTML
<form>
tags are inserted into the page containing the attributes you specified. (GET is applied by default, and so is not explicitly declared.) - Add an HTML table to the page. Under the HTML category in the Palette, click on a Table element and drag it to a point between the
<form>
tags. The Insert Table dialog box opens. Specify the following:
- Rows: 2
- Columns: 2
- Border Size: 0
- Cell Padding: 5
- Right-click inside the Source Editor and choose Format. This tidies up the code. Your form should now display similar to that below:
<form name="autofillform" action="autocomplete"> <table border="0" cellpadding="5"> <thead> <tr> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td></td> <td></td> </tr> <tr> <td></td> <td></td> </tr> </tbody> </table> </form>
- Within the first row of the table, type the following text into the first column (changes in bold):
<td><strong>Composer Name:</strong></td>
- Within the second column of the first row, instead of dragging a Text Input field from the Palette, type in the code below manually (changes in bold):
<td> <input type="text" size="40" id="complete-field" οnkeyup="doCompletion();"> </td>
When you type, try using the IDE's built-in code completion support. For example, type in<i
, then press Ctrl-Space. A list of suggested options displays below your cursor, and a description of the selected element appears in a box above. You can in fact press Ctrl-Space at anytime you are coding in the Source Editor to bring up possible options. Also, if there is only one possible option, pressing Ctrl-Space will automatically complete the element name.
The onkeyup
attribute that you typed in above points to a JavaScript function named doCompletion()
. This function is called each time a key is pressed in the form text field, and maps to the JavaScript call depicted in the Ajax flow diagram above.
Using the JavaScript Editor
The IDE's new JavaScript Editor provides many advanced editing capabilities, such as intelligent code completion, semantic highlighting, instant renaming and refactoring capabilities, as well as many more features. See Editing JavaScript for an introduction andhttp://wiki.netbeans.org/JavaScript for a detailed specification.
JavaScript code completion is automatically provided when you code in .js
files, as well as within <script>
tags when you work with other technologies (i.e., HTML, RHTML, JSP, PHP). When using the JavaScript Editor, the IDE provides you with browser-compatibility information, depending on the browser types and versions you specify in the JavaScript Options panel. Open the JavaScript Options panel by choosing Tools > Options (NetBeans > Preferences on Mac), then Miscellaneous > JavaScript.
![JavaScript Options panel JavaScript Options panel](http://netbeans.org/images_www/articles/70/web/ajax-intro/javascript-options.png)
The IDE provides out-of-the-box support for Firefox, Internet Explorer, Safari, and Opera. From the JavaScript Options panel, you can also specify the JavaScript engine version that code completion applies to.
Add a JavaScript file to the application and begin implementing doCompletion()
.
- In the Projects window, right-click on the project node and choose New > JavaScript file. (If JavaScript file is not listed, choose Other. Then choose JavaScript file from the Web category in the New File wizard.)
- Name the file
javascript
, and type inweb
in the Folder text field to place it in the web subfolder. - Click Finish and note that the new JavaScript file appears in the Projects window within the Web Pages folder.
Note: If you are wondering why the
web
folder is not displayed, click on the Files window (Window > Files). The Projects window provides a logical view of important project contents, whereas the Files window offers a representation of the entire project contents in a directory-based view. - Type the code below into
javascript.js
.var req; var isIE; function init() { completeField = document.getElementById("complete-field"); } function doCompletion() { var url = "autocomplete?action=complete&id=" + escape(completeField.value); req = initRequest(); req.open("GET", url, true); req.onreadystatechange = callback; req.send(null); } function initRequest() { if (window.XMLHttpRequest) { if (navigator.userAgent.indexOf('MSIE') != -1) { isIE = true; } return new XMLHttpRequest(); } else if (window.ActiveXObject) { isIE = true; return new ActiveXObject("Microsoft.XMLHTTP"); } }
The above code performs a simple browser compatibility check for Firefox 3 and Internet Explorer versions 6 and 7). If you would like to incorporate more robust code for compatibility issues, consider using this browser detect script from http://www.quirksmode.org.
- Switch back to
index.jsp
and add a reference to the JavaScript file between the<head>
tags.<script type="text/javascript" src="javascript.js"></script>
You can quickly toggle between pages opened in the Source Editor by pressing Ctrl-Tab.
- Insert a call to
init()
in the opening<body>
tag.<body οnlοad="init()">
This ensures thatinit()
is called each time the page is loaded.
The role of doCompletion()
is to:
- create a URL that contains data that can be utilized by the server-side,
- initialize an
XMLHttpRequest
object, and - prompt the
XMLHttpRequest
object to send an asynchronous request to the server.
The XMLHttpRequest
object is at the heart of Ajax and has become the de facto standard for enabling XML data to be passed asynchronously over HTTP. Asynchronous interaction implies that the browser can continue to process events in the page after the request is sent. Data is passed in the background, and can be automatically loaded into the page without requiring a page refresh.
Notice that the XMLHttpRequest
object is actually created by initRequest()
, which is called by doCompletion()
. The function checks whetherXMLHttpRequest
can be understood by the browser, and if so it creates an XMLHttpRequest
object. Otherwise, it performs a check onActiveXObject
(the XMLHttpRequest
equivalent for Internet Explorer 6), and creates an ActiveXObject
if identified.
Three parameters are specified when you create an XMLHttpRequest
object: a URL, the HTTP method (GET
or POST
), and whether or not the interaction is asynchronous. In the above example, the parameters are:
- The URL
autocomplete
, and the text entered into thecomplete-field
by the user:var url = "autocomplete?action=complete&id=" + escape(completeField.value);
GET
, signifying that HTTP interactions use theGET
method, andtrue
, signifying that the interaction is asynchronous:req.open("GET", url, true);
If the interaction is set as asynchronous, a callback function must be specified. The callback function for this interaction is set with the statement:
req.onreadystatechange = callback;
and a callback()
function must later be defined. The HTTP interaction begins when XMLHttpRequest.send()
is called. This action maps to the HTTP request that is sent to the web server in the above flow diagram.
Programming the Server-Side
The IDE provides comprehensive support for server-side web programming. While this includes basic editor support for many popular programming and scripting languages, it also encompasses web services, such as SOAP, REST, SaaS, and MVC-oriented frameworks, such as JSF, Spring, Struts, Ruby on Rails, and Grails. Ajax support is included in Woodstock components for JSF Visual Web projects, and Dynamic Faces, which is a JSF extension that provides AJAX functionality. There also exist numerous plugins for Ajax-driven frameworks, such as GWT, Struts2, and jMaki.
The application's business logic processes requests by retrieving data from the data store, then preparing and sending the response. This is implemented here using a servlet. Before you begin coding the servlet, set up the data store and the functionality required by the servlet to access data.
Creating the Data Store
For this simple application, you create a class called ComposerData
that retains composer data using a HashMap
. A HashMap
allows you to store pairs of linked items in key-value pairs. You also create a Composer
class that enables the servlet to retrieve data from entries in the HashMap
.
- Right-click on the project node in the Projects window and choose New > Java Class.
- Name the class
ComposerData
, and entercom.ajax
in the Package field. This creates a new package to contain the class, as well as other classes that will later be created. - Click Finish. The class is created and opens in the Source Editor.
- In the Source Editor, paste in the following code:
package com.ajax; import java.util.HashMap; /** * * @author nbuser */ public class ComposerData { private HashMap composers = new HashMap(); public HashMap getComposers() { return composers; } public ComposerData() { composers.put("1", new Composer("1", "Johann Sebastian", "Bach", "Baroque")); composers.put("2", new Composer("2", "Arcangelo", "Corelli", "Baroque")); composers.put("3", new Composer("3", "George Frideric", "Handel", "Baroque")); composers.put("4", new Composer("4", "Henry", "Purcell", "Baroque")); composers.put("5", new Composer("5", "Jean-Philippe", "Rameau", "Baroque")); composers.put("6", new Composer("6", "Domenico", "Scarlatti", "Baroque")); composers.put("7", new Composer("7", "Antonio", "Vivaldi", "Baroque")); composers.put("8", new Composer("8", "Ludwig van", "Beethoven", "Classical")); composers.put("9", new Composer("9", "Johannes", "Brahms", "Classical")); composers.put("10", new Composer("10", "Francesco", "Cavalli", "Classical")); composers.put("11", new Composer("11", "Fryderyk Franciszek", "Chopin", "Classical")); composers.put("12", new Composer("12", "Antonin", "Dvorak", "Classical")); composers.put("13", new Composer("13", "Franz Joseph", "Haydn", "Classical")); composers.put("14", new Composer("14", "Gustav", "Mahler", "Classical")); composers.put("15", new Composer("15", "Wolfgang Amadeus", "Mozart", "Classical")); composers.put("16", new Composer("16", "Johann", "Pachelbel", "Classical")); composers.put("17", new Composer("17", "Gioachino", "Rossini", "Classical")); composers.put("18", new Composer("18", "Dmitry", "Shostakovich", "Classical")); composers.put("19", new Composer("19", "Richard", "Wagner", "Classical")); composers.put("20", new Composer("20", "Louis-Hector", "Berlioz", "Romantic")); composers.put("21", new Composer("21", "Georges", "Bizet", "Romantic")); composers.put("22", new Composer("22", "Cesar", "Cui", "Romantic")); composers.put("23", new Composer("23", "Claude", "Debussy", "Romantic")); composers.put("24", new Composer("24", "Edward", "Elgar", "Romantic")); composers.put("25", new Composer("25", "Gabriel", "Faure", "Romantic")); composers.put("26", new Composer("26", "Cesar", "Franck", "Romantic")); composers.put("27", new Composer("27", "Edvard", "Grieg", "Romantic")); composers.put("28", new Composer("28", "Nikolay", "Rimsky-Korsakov", "Romantic")); composers.put("29", new Composer("29", "Franz Joseph", "Liszt", "Romantic")); composers.put("30", new Composer("30", "Felix", "Mendelssohn", "Romantic")); composers.put("31", new Composer("31", "Giacomo", "Puccini", "Romantic")); composers.put("32", new Composer("32", "Sergei", "Rachmaninoff", "Romantic")); composers.put("33", new Composer("33", "Camille", "Saint-Saens", "Romantic")); composers.put("34", new Composer("34", "Franz", "Schubert", "Romantic")); composers.put("35", new Composer("35", "Robert", "Schumann", "Romantic")); composers.put("36", new Composer("36", "Jean", "Sibelius", "Romantic")); composers.put("37", new Composer("37", "Bedrich", "Smetana", "Romantic")); composers.put("38", new Composer("38", "Richard", "Strauss", "Romantic")); composers.put("39", new Composer("39", "Pyotr Il'yich", "Tchaikovsky", "Romantic")); composers.put("40", new Composer("40", "Guiseppe", "Verdi", "Romantic")); composers.put("41", new Composer("41", "Bela", "Bartok", "Post-Romantic")); composers.put("42", new Composer("42", "Leonard", "Bernstein", "Post-Romantic")); composers.put("43", new Composer("43", "Benjamin", "Britten", "Post-Romantic")); composers.put("44", new Composer("44", "John", "Cage", "Post-Romantic")); composers.put("45", new Composer("45", "Aaron", "Copland", "Post-Romantic")); composers.put("46", new Composer("46", "George", "Gershwin", "Post-Romantic")); composers.put("47", new Composer("47", "Sergey", "Prokofiev", "Post-Romantic")); composers.put("48", new Composer("48", "Maurice", "Ravel", "Post-Romantic")); composers.put("49", new Composer("49", "Igor", "Stravinsky", "Post-Romantic")); composers.put("50", new Composer("50", "Carl", "Orff", "Post-Romantic")); } }
Create the Composer
class:
- Right-click on the project node in the Projects window and choose New > Java Class.
- Name the class
Composer
, and choosecom.ajax
from the drop-down list in the Package field. Click Next. - Click Finish. The class is created and opens in the Source Editor.
- In the Source Editor, paste in the following code:
package com.ajax; public class Composer { private String id; private String firstName; private String lastName; private String category; public Composer (String id, String firstName, String lastName, String category) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.category = category; } public String getCategory() { return category; } public String getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } }
Creating a Servlet
Create a servlet to handle the autocomplete
URL that is received by the incoming request.
- Right-click on the project node in the Projects window and choose New > Servlet. (If Servlet is not displayed, choose Other, then select Servlet from the Web category.)
- Name the servlet
AutoCompleteServlet
, and choosecom.ajax
from the drop-down list in the Package field. Click Next. - In the Configure Servlet Deployment panel, note that the servlet name is
AutoCompleteServlet
and the fully-qualified path to the class iscom.ajax.AutoCompleteServlet
. Change the URL pattern to/autocomplete
so that it matches the URL that you previously set in theXMLHttpRequest
object. This panel saves you the step of needing to manually add these details to the deployment descriptor. - Click Finish. The servlet is created and opens in the Source Editor. The
web.xml
deployment descriptor is also updated to indicate that the servlet is registered with the application.
The only methods you need to override are doGet()
, to define how the servlet handles the autocomplete
GET
request, and init()
, which needs to initiate a ServletContext
so the servlet can access other classes in the application once it is put into service.
You can override methods from superclasses using the IDE's Insert Code popup menu. Implement init()
by doing this.
- Place your cursor beneath the
AutoCompleteServlet
class declaration in the Source Editor. Press Alt-Insert (Ctrl-I on Mac) to open the Generate Code popup menu.
- Choose Override Method. In the dialog that displays, all classes that
AutoCompleteServlet
inherits from are displayed. Expand the GenericServlet node and selectinit(Servlet Config arg0)
.
- Click OK. The
init()
method is added in the Source Editor. - Add a variable for a
ServletContext
object and modifyinit()
(changes in bold):private ServletContext context; @Override public void init(ServletConfig config) throws ServletException { this.context = config.getServletContext(); }
- Add an import statement for
ServletContext
. You can do this by clicking on the lightbulb icon that displays in the left margin of the Source Editor
The doGet()
method needs to parse the URL of the request, retrieve data from the data store, and prepare a response in XML format. Note that the method declaration was generated when you created the class. To view it, you may need to expand the HttpServlet methods by clicking on the expand icon () in the left margin.
- Add the following variable declarations beneath the
AutocompleteServlet
class declaration.private ComposerData compData = new ComposerData(); private HashMap composers = compData.getComposers();
This creates aHashMap
of all composer data, which is then employed bydoGet()
. - Scroll down to
doGet()
and implement the method as follows:@Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String action = request.getParameter("action"); String targetId = request.getParameter("id"); StringBuffer sb = new StringBuffer(); if (targetId != null) { targetId = targetId.trim().toLowerCase(); } else { context.getRequestDispatcher("/error.jsp").forward(request, response); } boolean namesAdded = false; if (action.equals("complete")) { // check if user sent empty string if (!targetId.equals("")) { Iterator it = composers.keySet().iterator(); while (it.hasNext()) { String id = (String) it.next(); Composer composer = (Composer) composers.get(id); if ( // targetId matches first name composer.getFirstName().toLowerCase().startsWith(targetId) || // targetId matches last name composer.getLastName().toLowerCase().startsWith(targetId) || // targetId matches full name composer.getFirstName().toLowerCase().concat(" ") .concat(composer.getLastName().toLowerCase()).startsWith(targetId)) { sb.append("<composer>"); sb.append("<id>" + composer.getId() + "</id>"); sb.append("<firstName>" + composer.getFirstName() + "</firstName>"); sb.append("<lastName>" + composer.getLastName() + "</lastName>"); sb.append("</composer>"); namesAdded = true; } } } if (namesAdded) { response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write("<composers>" + sb.toString() + "</composers>"); } else { //nothing to show response.setStatus(HttpServletResponse.SC_NO_CONTENT); } } }
As you can see in this servlet, there is nothing really new you need to learn to write server-side code for Ajax processing. The response content type needs to be set to text/xml
for cases where you want to exchange XML documents. With Ajax, you can also exchange plain text or even snippets of JavaScript which may be evaluated or executed by the callback function on the client. Note too that some browsers might cache the results, and so it may be necessary to set the Cache-Control HTTP header to no-cache
.
In this example, the servlet generates an XML document that contains all composers with a first or last name beginning with the characters typed in by the user. This document maps to the XML Data depicted in the flow diagram above. Here is an example of an XML document that is returned to the XMLHttpRequest
object:
<composers> <composer> <id>12</id> <firstName>Antonin</firstName> <lastName>Dvorak</lastName> </composer> <composer> <id>45</id> <firstName>Aaron</firstName> <lastName>Copland</lastName> </composer> <composer> <id>7</id> <firstName>Antonio</firstName> <lastName>Vivaldi</lastName> </composer> <composer> <id>2</id> <firstName>Arcangelo</firstName> <lastName>Corelli</lastName> </composer> </composers>
You can use the IDE's HTTP Monitor to view the returned XML data once you complete the application.
Programming the Client-Side: Part 2
You must define the callback function to handle the server's response, and add any functionality necessary to reflect changes in the page that is viewed by the user. This requires modifying the HTML DOM. Finally, you can work in the IDE's CSS Editor to add a simple stylesheet to the presentation.
Adding Callback Functionality
The callback function is called asynchronously at specific points during HTTP interaction when the readyState
property of the XMLHttpRequest
object changes. In the application you are building, the callback function is callback()
. You recall that in doCompletion()
, callback
was set as the XMLHttpRequest.onreadystatechange
property to a function. Now, implement the callback function as follows.
- Open
javascript.js
in the Source Editor and type in the code below.function callback() { if (req.readyState == 4) { if (req.status == 200) { parseMessages(req.responseXML); } } }
A readyState
of "4" signifies the completion of the HTTP interaction. The API for XMLHttpRequest.readState
indicates that there are 5 possible values that can be set. These are:
readyState Value | Object Status Definition |
---|---|
0 | uninitialized |
1 | loading |
2 | loaded |
3 | interactive |
4 | complete |
Notice that the parseMessages()
function is called only when the XMLHttpRequest.readyState
is "4" and the status
-- the HTTP status code definition of the request -- is "200", signifying a success. You will define parseMessages()
next in Updating the HTML DOM.
Updating the HTML DOM
The parseMessages()
function handles the incoming XML data. In doing so, it relies on several ancillary functions, such as appendComposer()
,getElementY()
, and clearTable()
. You must also introduce new elements to the index page, such as a second HTML table which serves as the auto-complete box, and ID's for elements so they can be referenced in javascript.js
. Finally, you create new variables corresponding to ID's for elements in index.jsp
, initialize them in the init()
function that you previously implemented, and add some functionality that is needed each timeindex.jsp
is loaded.
Note: The functions and elements that you create in the following steps work interdependently. It is recommended that you work through this section, then examine the code once it is all in place.
- Open
index.jsp
in the Source Editor and type in the below code for the second row of the HTML table you previously created.<tr> <td id="auto-row" colspan="2"> <table id="complete-table" /> <td/> </tr>
The second row of the table contains another HTML table. This table represents the auto-complete box that will be used to populate composer names. - Open
javascript.js
in the Source Editor and the following three variables to the top of the file.var completeField; var completeTable; var autoRow;
- Add the following lines (in bold) to the
init()
function.function init() { completeField = document.getElementById("complete-field"); completeTable = document.getElementById("complete-table"); autoRow = document.getElementById("auto-row"); completeTable.style.top = getElementY(autoRow) + "px"; }
One purpose ofinit()
is to make elements insideindex.jsp
accessible to other functions that will modify the index page's DOM. - Add
appendComposer()
tojavascript.js
.function appendComposer(firstName,lastName,composerId) { var row; var cell; var linkElement; if (isIE) { completeTable.style.display = 'block'; row = completeTable.insertRow(completeTable.rows.length); cell = row.insertCell(0); } else { completeTable.style.display = 'table'; row = document.createElement("tr"); cell = document.createElement("td"); row.appendChild(cell); completeTable.appendChild(row); } cell.className = "popupCell"; linkElement = document.createElement("a"); linkElement.className = "popupItem"; linkElement.setAttribute("href", "autocomplete?action=lookup&id=" + composerId); linkElement.appendChild(document.createTextNode(firstName + " " + lastName)); cell.appendChild(linkElement); }
This function creates a new table row, inserts a link to a composer into it using the data passed to the function via its three parameters, and inserts the row into the index page'scomplete-table
element. - Add
getElementY()
tojavascript.js
.function getElementY(element){ var targetTop = 0; if (element.offsetParent) { while (element.offsetParent) { targetTop += element.offsetTop; element = element.offsetParent; } } else if (element.y) { targetTop += element.y; } return targetTop; }
This function is applied to find the vertical position of the parent element. This is necessary because the actual position of the element, when it is displayed, is often dependent on browser type and version. Note that thecomplete-table
element, when displayed containing composer names, is shifted to the lower right of the table in which it exists. The correct height positioning is determined bygetElementY()
.
Note: See this explanation of
offset
on http://www.quirksmode.org/. - Add
clearTable()
tojavascript.js
.function clearTable() { if (completeTable.getElementsByTagName("tr").length > 0) { completeTable.style.display = 'none'; for (loop = completeTable.childNodes.length -1; loop >= 0 ; loop--) { completeTable.removeChild(completeTable.childNodes[loop]); } } }
This function sets the display of thecomplete-table
element to 'none', (i.e., makes it invisible), and it removes any existing composer name entries that were created. - Modify the
callback()
function to callclearTable()
each time new data is received from the server. Any composer entries that already exist in the auto-complete box are therefore removed before it becomes populated with new entries.function callback() { clearTable(); if (req.readyState == 4) { if (req.status == 200) { parseMessages(req.responseXML); } } }
- Add
parseMessages()
tojavascript.js
.function parseMessages(responseXML) { // no matches returned if (responseXML == null) { return false; } else { var composers = responseXML.getElementsByTagName("composers")[0]; if (composers.childNodes.length > 0) { completeTable.setAttribute("bordercolor", "black"); completeTable.setAttribute("border", "1"); for (loop = 0; loop < composers.childNodes.length; loop++) { var composer = composers.childNodes[loop]; var firstName = composer.getElementsByTagName("firstName")[0]; var lastName = composer.getElementsByTagName("lastName")[0]; var composerId = composer.getElementsByTagName("id")[0]; appendComposer(firstName.childNodes[0].nodeValue, lastName.childNodes[0].nodeValue, composerId.childNodes[0].nodeValue); } } } }
The parseMessages()
function receives as a parameter an object representation of the XML document returned by the AutoComplete
servlet. The function programmatically traverses the XML document, extracting the firstName
, lastName
, and id
of each entry, then passes this data toappendComposer()
. This results in a dynamic update to the contents of the complete-table
element. For example, an entry that is generated and inserted into complete-table
might look as follows:
<tr> <td class="popupCell"> <a class="popupItem" href="autocomplete?action=lookup&id=12">Antonin Dvorak</a> </td> </tr>
The dynamic update to the complete-table
element represents the final step of the process flow of communication that takes place during communication using Ajax. This update maps to the HTML & CSS data being sent to the presentation in the flow diagram above.
Attaching a Stylesheet
At this stage, you have completed all the code needed for the functionality of the application. To see the results of your efforts, try running the application now.
- In the Projects window, right-click the project node and choose Run. The project is recompiled and deployed to the target server. Your browser opens, and you can see the index page.
To add attach a stylesheet to your application, simply create a .css
file and link to it from your presentation page(s). When you work in .css
files, the IDE provides you with code completion support, as well as several other features that can aid in producing stylesheet rules. These include:
- Style Rule Editor: A dialog enabling you to create rules based on classes, ID's and HTML elements, and set their position in the document hierarchy.
- CSS Preview window: A preview window which, when you place your cursor within a rule, displays sample text rendered according to the declaration block of that rule.
- CSS Style Builder: An interface designed to enable you to create rules using a selection of controls and widgets.
To attach a stylesheet to your application do the following.
- In the Projects window, right-click on the project node and choose New > Cascading Style Sheet (If Cascading Style Sheet is not listed, choose Other. Then choose Cascading Style Sheet from the Web category in the New File wizard.)
- In the CSS File Name text field, type in
stylesheet
. - Click Finish. The new file is added to the Projects window, and opens in the IDE's editor.
- In
stylesheet.css
, type in the following rules. You can make use of the IDE's code completion support by pressing Ctrl-Space at points when you want to call up suggestions.body { font-family: Verdana, Arial, sans-serif; font-size: smaller; padding: 50px; color: #555; width: 650px; } h1 { letter-spacing: 6px; font-size: 1.6em; color: #be7429; font-weight: bold; } h2 { text-align: left; letter-spacing: 6px; font-size: 1.4em; color: #be7429; font-weight: normal; width: 450px; } table { width: 550px; padding: 10px; background-color: #c5e7e0; } td { padding: 10px; } a { color: #be7429; text-decoration: none; } a:hover { text-decoration: underline; } .popupBox { position: absolute; top: 170px; left: 140px; } .popupCell { background-color: #fffafa; } .popupCell:hover { background-color: #f5ebe9; } .popupItem { color: #333; text-decoration: none; font-size: 1.2em; }
Perform a check on the validity of your CSS code by right-clicking in the CSS Editor and choosing Check CSS. Any errors encountered are displayed in the Output window (Windows > Output).
- Open the CSS Preview window by choosing Window > Other > Css Preview.
- Place your cursor within a rule that makes adjustments to text and color (e.g.,
h1
). The CSS Preview window generates a sample display depicting how the text will render in a browser.
- Switch to the
index.jsp
page in the Source Editor, and add a reference to the stylesheet between the<head>
tags.<link rel="stylesheet" type="text/css" href="stylesheet.css">
- Add the
popupBox
class that is defined in the stylesheet to thecomplete-table
element (changes in bold).<tr> <td id="auto-row" colspan="2"> <table id="complete-table" class="popupBox" /> <td/> </tr>
As indicated instylesheet.css
, this rule positions thecomplete-table
element so that it displays slightly to the right of its parent element.
Running the Project
When you run the application again, it displays in the browser using the stylesheet you just created. Each time you type in a character, an asynchronous request is sent to the server, and returned with XML data that has been prepared by AutoCompleteServlet
. As you enter more characters, the number of composer names decreases to reflect the new list of matches.
Using the HTTP Server Monitor
You can use the IDE's HTTP Server monitor to verify the HTTP communication that takes place as requests and responses are passed between the client and server. The HTTP Server Monitor displays information such as client and server headers, session properties, cookie details, as well as request parameters.
Before you begin using the HTTP monitor, you must first enable it on the server you are using.
- Open the Servers window by choosing Tools > Servers from the main menu.
- In the left pane, select the server you are using with the project. Then, in the right pane, select the Enable HTTP Monitor option.
Note: This option displays under the Common tab for the GlassFish server. For Tomcat, it resides under the Connection tab.
- Click Close.
If the server is already running, you need to restart it in order to allow changes to take effect. You can restart the server by opening the Services window (Window > Services), then right-clicking your server beneath the Servers node and choosing Restart.
Now, when you run the application again, the HTTP monitor opens in the lower region of the IDE. You can select a record in the left pane, then click the tabs in the main window to view information concerning each request that is made.
![HTTP Server monitor displays in the IDE HTTP Server monitor displays in the IDE](http://netbeans.org/images_www/articles/70/web/ajax-intro/http-monitor.png)
You can verify the XML data that is sent from the server as a result of the asynchronous request made when a user enters a character in the auto-complete field.
- In the tree view on the left side of the HTTP monitor, right-click a request record and choose Replay.
The response is generated in your browser. In this case, because the response consists of XML data, the browser displays the data in it's native XML viewer.
![HTTP Server monitor displays in the IDE HTTP Server monitor displays in the IDE](http://netbeans.org/images_www/articles/70/web/ajax-intro/xml-data.png)
Conclusion
This concludes the Introduction to Ajax. Hopefully by now you realize that Ajax is simply exchanging information over HTTP in the background, and updating that page dynamically based on the results.
You may note that the application you built has numerous shortcomings, for example nothing happens when a composer name is selected from the auto-complete box! You are welcome to download the solution project to see how this can be implemented using JSP technology. Furthermore, you might want to investigate server-side validation that prevents a user from requesting a name that does not exist in the data store. You can learn more about these techniques and technologies by following other tutorials on the Java EE & Java Web Learning Trail.
转自:http://netbeans.org/kb/docs/web/ajax-quickstart.html#client1