AJAX Basics and Development Tools

Sang Shin, www.javapassion.com/ajaxcodecamp


This hands-on lab takes you through the basics of AJAX, especially focusing on the usage of XMLHttpRequest JavaScript object, which facilitates asynchronous communication between a browser and a server-side Web application.  In production environment, it is likely that you would not use XMLHttpRequest JavaScript object directly, because it is considered as a low-level plumbing piece. Instead you would use one or combination of the following frameworks and toolkits:

Yet, in order to develop well-functioning AJAX applications, it is important for you to have a good understanding on how XMLHttpRequest works.   This lab is designed to help you acquire that understanding.

In this lab, you will also learn how to use various JavaScript debugging tools listed below for debugging client-side of your AJAX application.  The lab is not meant to have exhaustive coverage on these tools, however.  (By the way, the "Firebug" JavaScript Debugger is the most comprehensive and most useful.)

Expected duration: 180 minutes

Prerequisites

This hands-on lab assumes you have some basic knowledge of, or programming experience with, the following technologies.


Software Needed

Before you begin, you need to install the following software on your computer. 


OS platforms you can use


Change Log

 

Lab Exercises


You will need an internet connection for Exercise 0 in which you will do further installation and configuration.  From Exercise 1, you can proceed without an internet connection.

Exercise 0: Installation and Configuration

In this exercise, you are going to configure Mozilla Firefox browser to be the default browser of NetBeans IDE.   Make sure you have downloaded and installed the software mentioned above before you start.

    1. Install Firebug over Firefox v3.x
    2. Set Mozilla Firefox browser to be the default browser of NetBeans

(0.1) Install Firebug over Firefox v3.x


1. From your Firefox browser, go to http://getfirebug.com/.









2. Enable Firebug panels.









(0.2) Set Mozilla Firefox browser to be the default browser of NetBeans IDE


In this step, you are going to select the Firefox browser as the default browser of the NetBeans IDE.  What this means is that every time a browser is started by NetBeans, the Firefox browser will be displayed.

1. Start NetBeans IDE.
2. Select Tools - or Netbeans on Mac OS X - from the top-level menu bar of NetBeans IDE.
3. Select Options - or Preferences on Mac OS X. The Options dialog box appears.



4. Select Firefox for the Web Browser. (Or you can use <Default System Browser> if the default system browser is configured to be Firefox.)
5. Click OK to close the Options dialog box. (Figure-0.10 below)


Figure-0.10: Configure Firefox as default browser for the NetBeans


                                                                                                                    return to the top


Exercise 1: Build, run, and debug "Data Validation with AJAX" sample application

In this exercise, you are going to build and run "Data Validation with AJAX" sample application and then look under the hood on how things are working, especially on how XMLHttpRequest JavaScript object is created and then used by the browser for performing asynchronous communication with the back-end servlet. 

You will also learn how to use JavaScript's "alert" messaging and Firefox's built-in JavaScript console for debugging your client-side JavaScript code.

  1. Create and run a NetBeans project called ajax-validation from the "Data validation with AJAX" sample application
  2. Look under the hood of the sample application
  3. Implant "alert" messages to the client JavaScript code to understand the control flow
  4. Learn how to use JavaScript Console within the Firefox browser

(1.1) Open, build, and run "Data validation with AJAX" sample application (named as "ajax-validation" NetBeans project)

1.  Open ajax-validation NetBeans project. 



Figure-1.10: Open ajax-validation NetBeans project

2. Build and run ajax-validation project


Figure-1.12: Run the project

3.  The browser gets displayed.  Type some string like sa into the User Id: field. You will notice green-colored Valid User Id string gets displayed.   (Figure-1.13 below)  Here every character you type in the User Id: field is validated by the server side validation logic asynchronously.


Figure-1.13: Running Data Validation using AJAX

4.  Type duke into the User Id: field. You will notice Valid User Id string gets displayed then Invalid User Id gets displayed when all 4 characters of duke are typed in.  You will also notice that the Create Account button is grayed out and disabled. (Figure-1.14)


Figure-1.14: Running Data Validation using AJAX

                                                                                                                    return to top of the exercise
'

(1.2) Look under the hood of the sample application

In this step, you will look under the hood of the sample AJAX application you just built and ran in the previous step.  You will learn how XMLHttpRequest JavaScript object is created and then used for performing asynchronous communication with the back-end servlet.

1.  Double-click index.jsp under ajax-validation->Web Pages to open it in the source editor of NetBeans IDE.   Comments, which are highlighted in bold font, are added below (but not actual code you see in the NetBeans browser) in order to aid your understanding on how things are working.  The sequence of how function calls are invoked are also specified with the (red colored number in parentheses).

Please spend some time reading the code and associated comments.  Note that index.jsp is client side code - meaning the JavaScript code gets executed by the browser.

<%-- Copyright 2005 Sun Microsystems, Inc. All rights reserved. You may not modify, use, reproduce, or distribute this software except in compliance with the terms of the License at: http://developer.sun.com/berkeley_license.html
$Id: index.jsp,v 1.4 2005/06/15 05:39:43 gmurray71 Exp $ --%>
<html>
<head>

<!-- JavaScript code starts from here -->
<script type="text/javascript">
var req;
var target;
var isIE;

// (3) JavaScript function in which XMLHttpRequest JavaScript object is created.
// Please note that depending on a browser type, you create
// XMLHttpRequest JavaScript object differently.  Also the "url" parameter is not
// used in this code (just in case you are wondering why it is
// passed as a parameter).
function initRequest(url) {
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        isIE = true;
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

// (2) Event handler that gets invoked whenever a user types a character
// in the input form field whose id is set as "userid".  This event
// handler invokes "initRequest(url)" function above to create XMLHttpRequest
// JavaScript object.
function validateUserId() {
    if (!target) target = document.getElementById("userid");
    var url = "validate?id=" + escape(target.value);   

    // Invoke initRequest(url) to create XMLHttpRequest object
    initRequest(url);

    // The "processRequest" function is set as a callback function.
    // (Please note that, in JavaScript, functions are first-class objects: they
    // can be passed around as objects.  This is different from the way
    // methods are treated in Java programming language.)
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);
}

// (4) Callback function that gets invoked asynchronously by the browser
// when the data has been successfully returned from the server.
// (Actually this callback function gets called every time the value
// of "readyState" field of the XMLHttpRequest object gets changed.)
// This callback function needs to be set to the "onreadystatechange"
// field of the XMLHttpRequest.
function processRequest() {
    if (req.readyState == 4) {
        if (req.status == 200) {
 
            // Extract "true" or "false" from the returned data from the server.
            // The req.responseXML should contain either <valid>true</valid> or <valid>false</valid>
            var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            // Call "setMessageUsingDOM(message)" function to display
            // "Valid User Id" or "Invalid User Id" message.
            setMessageUsingDOM(message);
            // If the user entered value is not valid, do not allow the user to
            // click submit button.
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
        }
    }
}

// This function is not used for now. You will use this later.
function setMessageUsingInline(message) {
    mdiv = document.getElementById("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style=\"color:red\">Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style=\"color:green\">Valid User Id</div>";
    } 
}


// (5) Function in which message indicating the validity of the data gets displayed
// through the "userIdMessage" <div> element.
function setMessageUsingDOM(message) {
     var userMessageElement = document.getElementById("userIdMessage");
     var messageText;
     if (message == "false") {
         userMessageElement.style.color = "red";
         messageText = "Invalid User Id";
     } else {
         userMessageElement.style.color = "green";
         messageText = "Valid User Id";
     }
     var messageBody = document.createTextNode(messageText);
     // if the messageBody element has been created simple replace it otherwise
     // append the new element
     if (userMessageElement.childNodes[0]) {
         userMessageElement.replaceChild(messageBody, userMessageElement.childNodes[0]);
     } else {
         userMessageElement.appendChild(messageBody);
     }
 }

function disableSubmitBtn() {
    var submitBtn = document.getElementById("submit_btn");
    submitBtn.disabled = true;
}
</script>
 <title>Form Data Validation using AJAX</title>
</head>

 <body onload="disableSubmitBtn()">
 
 <h1>Form Data Validation using AJAX</h1>
 <hr/>
 <p>
 This example shows how you can use AJAX to do server-side form data validation without
 a page reload.
 </p>
 <p>
 In the form below enter a user id. By default the user ids &quot;greg&quot; and &quot;duke&quot;
 are taken. If you attempt to enter a user id that has been taken an error message will be
 displayed next to the form field and the &quot;Create Account&quot; button will be
 disabled. After entering a valid user id and selecting the &quot;Create Account&quot;
 button that user id  will be added to the list of user ids that are taken.
 </p>
 
  <form name="updateAccount" action="validate" method="post">
   <input type="hidden" name="action" value="create"/>
   <table border="0" cellpadding="5" cellspacing="0">
    <tr>
     <td><b>User Id:</b></td>
     <td>
      <!-- (1) Input form field whose id is set as "userid" and "validateUserId()" function is
             associated with the onkeyup event -->
      <input    type="text"
                size="20" 
                  id="userid"
                name="id"
             onkeyup="validateUserId()">
     </td>
     <!-- The "userIdMessage" div element specifies the location where input validation
            message gets displayed. -->
     <td>
      <div id="userIdMessage"></div>
     </td>
    </tr>
    <tr>
     <td align="right" colspan="2">
      <input id="submit_btn" type="Submit" value="Create Account">
     </td>
     <td></td>
    </tr>
   </table>
  </form>
 </body>
</html>
          Code-1.20: index.jsp

2. Double-click ValidationServlet.java under ajax-validation->Source Packages->com.sun.j2ee.blueprints.bpcatalog.ajax to open it in the source editor of NetBeans IDE.  You should see the Code-1.21 below.  Comments, which are highlighted in bold font, are added below (but not actual code you see in the NetBeans browser) in order to aid your understanding on how things are working.

Note that ValidationServlet.java is the server side code meaning this code gets executed on the J2EE runtime platform such as Sun Java System App Server. 

package com.sun.j2ee.blueprints.bpcatalog.ajax;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

public class ValidationServlet extends HttpServlet {
   
    private ServletContext context;
    private HashMap accounts = new HashMap();
 
    // Initialize the "accounts" hashmap.  For the sake of this exercise,
    // two accounts are created with names "greg" and "duke" during
    // initialization of the Servlet.
    public void init(ServletConfig config) throws ServletException {
        this.context = config.getServletContext();
        accounts.put("greg","account data");
        accounts.put("duke","account data");
    }
   
    public  void doGet(HttpServletRequest request, HttpServletResponse  response)
        throws IOException, ServletException {
       
        // Extract the data of the input form field whose name is "id"
        String targetId = request.getParameter("id");

        //  Send back either "<valid>true</valid>" or "<valid>false</valid>"
        //  XML message depending on the validity of the data that was entered.
        //  Note that the content type is "text/xml".
        if ((targetId != null) && !accounts.containsKey(targetId.trim())) {
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().write("<valid>true</valid>");
        } else {
            response.setContentType("text/xml");
            response.setHeader("Cache-Control", "no-cache");
            response.getWriter().write("<valid>false</valid>");
        }
    }

    public  void doPost(HttpServletRequest request, HttpServletResponse  response)
        throws IOException, ServletException {

        String targetId = request.getParameter("id");
        if ((targetId != null) && !accounts.containsKey(targetId.trim())) {
            accounts.put(targetId.trim(), "account data");
            request.setAttribute("targetId", targetId);
            context.getRequestDispatcher("/success.jsp").forward(request, response);
        } else {
            context.getRequestDispatcher("/error.jsp").forward(request, response);
        }
    }
   
}
          Code-1.21: ValidationServlet.java

                                                                                                                    return to top of the exercise


(1.3) Implant several "alert" messages in the client JavaScript code to understand the control flow


In this step, we will implant several "alert" messages in the index.jsp file so that we can see what is happening under the hood.

1. Modify the index.jsp to insert several "alert" messages as shown below.  The alert messages that need to be added are highlighted in bold and red colored font. (There are 4 places you will insert "alert" messages.)

Note: In general, you would want to use logging facility of Firebug debugger instead of "alert" messages.  You will learn how to use logging facility of Firebug debugger later on.

<%-- Copyright 2005 Sun Microsystems, Inc. All rights reserved. You may not modify, use, reproduce, or distribute this software except in compliance with the terms of the License at: http://developer.sun.com/berkeley_license.html
$Id: index.jsp,v 1.4 2005/06/15 05:39:43 gmurray71 Exp $ --%>
<html>
<head>

<!-- JavaScript code starts from here -->
<script type="text/javascript">
var req;
var target;
var isIE;

// Function in which XMLHttpRequest JavaScript object is created.
// Please note that depending on a browser type, you create
// XMLHttpRequest differently.
function initRequest(url) {
    alert("initRequest(" + url + ") is called");
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        isIE = true;
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

// Event handler that gets invoked whenever a user types a character
// in the input form field whose id is set as "userid".  This event
// handler invokes "initRequest(url)" function above to create XMLHttpRequest
// JavaScript object.
function validateUserId() {
    alert("validateUserId() is called");
    if (!target) target = document.getElementById("userid");
    var url = "validate?id=" + escape(target.value);  

    // Invoke initRequest(url) to create XMLHttpRequest object
    initRequest(url);

    // The "processRequest" function is set as a callback function.
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);
}

// Callback function that gets invoked asynchronously by the browser
// when the data has been successfully returned from the server.
// (Actually this callback function gets called every time the value
// of "readyState" field of the XMLHttpRequest object gets changed.)
// This callback function needs to be set to the "onreadystatechange"
// field of the XMLHttpRequest.
function processRequest() {
    alert("processRequest() is called, req.readyState = " + req.readyState + "  req.status = " + req.status);
    if (req.readyState == 4) {
        if (req.status == 200) {
 
            // Extract "true" or "false" from the returned data from the server.
            var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            // Call "setMessageUsingDOM(message)" function to display
            // "Valid User Id" or "Invalid User Id" message.
            setMessageUsingDOM(message);
            // If the user entered value is not valid, do not allow the user to
            // click submit button.
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
        }
    }
}

// This function is not used.
function setMessageUsingInline(message) {
    mdiv = document.getElementById("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style=\"color:red\">Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style=\"color:green\">Valid User Id</div>";
    }
}

// Function in which message indicating the validity of the data gets displayed
// through the "userIdMessage" element.
function setMessageUsingDOM(message) {
     alert("setMessageUsingDOM(" + message +") is called");
     var userMessageElement = document.getElementById("userIdMessage");
     var messageText;
     if (message == "false") {
         userMessageElement.style.color = "red";
         messageText = "Invalid User Id";
     } else {
         userMessageElement.style.color = "green";
         messageText = "Valid User Id";
     }
     var messageBody = document.createTextNode(messageText);
     // if the messageBody element has been created simple replace it otherwise
     // append the new element
     if (userMessageElement.childNodes[0]) {
         userMessageElement.replaceChild(messageBody, userMessageElement.childNodes[0]);
     } else {
         userMessageElement.appendChild(messageBody);
     }
 }

function disableSubmitBtn() {
    var submitBtn = document.getElementById("submit_btn");
    submitBtn.disabled = true;
}
</script>
 <title>Form Data Validation using AJAX</title>
</head>


 <body onload="disableSubmitBtn()">
 
 <h1>Form Data Validation using AJAX</h1>
 <hr/>
 <p>
 This example shows how you can use AJAX to do server-side form data validation without
 a page reload.
 </p>
 <p>
 In the form below enter a user id. By default the user ids &quot;greg&quot; and &quot;duke&quot;
 are taken. If you attempt to enter a user id that has been taken an error message will be
 displayed next to the form field and the &quot;Create Account&quot; button will be
 disabled. After entering a valid user id and selecting the &quot;Create Account&quot;
 button that user id  will be added to the list of user ids that are taken.
 </p>
 
  <form name="updateAccount" action="validate" method="post">
   <input type="hidden" name="action" value="create"/>
   <table border="0" cellpadding="5" cellspacing="0">
    <tr>
     <td><b>User Id:</b></td>
     <td>
      <!-- Input form field whose id is set as "userid" and "validateUserId()" function is
             associated with the onkeyup event -->
      <input    type="text"
                size="20"
                  id="userid"
                name="id"
             onkeyup="validateUserId()">
     </td>
     <!-- The "userIdMessage" div element specifies the location where input validation
            message gets displayed. -->
     <td>
      <div id="userIdMessage"></div>
     </td>
    </tr>
    <tr>
     <td align="right" colspan="2">
      <input id="submit_btn" type="Submit" value="Create Account">
     </td>
     <td></td>
    </tr>
   </table>
  </form>
 </body>
</html>

2. Right-click ajax-validation project and select Run.  The Firefox browser will be updated.  If the Firefox browser does not get displayed as a front window, just open the Firefox browser manually.

3. Enter a character in the User Id: field in the browser. You should see an alert message from the validateUserId() function. Click OK to close the alert message box. (Figure-1.25 below)


4. Now you should see an alert message from initRequest() function.  The url parameter has value of "validate?id=x". (Figure-1.26 below).  Click OK to close the alert message box.


Figure-1.26: initRequest() function gets called from the validateUserId() function

5. The processRequest() function gets called every time the value of the readyState field of the XMLHttpRequest object gets changed.  It is changed to 2. (Figure-1.27 below) It indicates that the receiving of the data from the the page is loaded. Click OK to close the alert message box.


Figure-1.27: readyState of XMLHttpRequest is changed to 2

6.  The processRequest() function gets called every time the value of the readyState field of the XMLHttpRequest object gets changed.  It is changed to 3. (Figure-1.28 below) It indicates that the receiving of the data from the server is partially done. Click OK to close the alert message box.


Figure-1.28: readyState of XMLHttpRequest is changed to 3

7. The processRequest() function gets called every time the value of the readyState field of the XMLHttpRequest object gets changed.  It is changed to 4. (Figure-1.29 below)  It indicates that the receiving of the data from the server is completed. Click OK to close the alert message box.


Figure-1.29: readyState of XMLHttpRequest is changed to 4

8. Alert message from setMessageUsingDOM() is called. (Figure-1.30 below) This will display "Valid User Id" or "Invalid User Id" message. (Figure-1.x below) Click OK to close the alert message box.


Figure-1.30: setMessageUsingDOM(true) is called

9. "Valid User Id" message gets displayed. (Figure-1.31 below)


Figure-1.31: "Valid User Id" message gets displayed


10. Type another character into the User Id: field and observe the same set of alert messages get displayed.
11. Remove alert messages from the index.jsp file if you so desire.

                                                                                                                    return to top of the exercise


(1.4) Learn how to use "Error Console" within the Firefox browser

In this step, you will learn how to use the built-in JavaScript Console from the Firefox browser for debugging JavaScript code.

1. Modify index.jsp file to plant JavaScript code fragment that is syntactically incorrect. 

In the code below, XMLHttpRequest() is changed to XmlHttpRequest() for the sake of this exercise - if it is not that obvious to you, the capital letters of XML is changed to Xml.  Since JavaScript code is case sensitive like Java programming language, this should result in a syntax error.

// Function in which XMLHttpRequest JavaScript object is created.
// Please note that depending on a browser type, you create
// XMLHttpRequest differently.
function initRequest(url) {
    //alert("initRequest(" + url + ") is called");
    if (window.XMLHttpRequest) {
        //req = new XMLHttpRequest();  
        req = new XmlHttpRequest();       
    } else if (window.ActiveXObject) {
        isIE = true;
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

2. Right-click ajax-validation project and select Run.  The browser will get displayed.
3. Within the Firefox browser, select Tools from the menu-bar and then select Error Console. (Figure-1.42 below)  The Error Console will be opened.  (If you are using Firefox 1.x, the Error Console is called JavaScript Console.)


Figure-1.42: Open JavaScript Console

4. Enter a character in the User Id: field. You will notice that there is not any "Valid User Id" display message.  So something is not working.
5. Take a look at the Error Console.  You will see the error condition that was found by the Error Console.  In this case, the "XmlHttpRequest is not defined" message  indicated that you are calling a variable or a function that is not defined.

Note: If you have Firebug debugger enabled, you will see the same error message in the Console window of the Firebug debugger not in the Error Console of the Firefox browser.  That is good enough for this exercise.  We will learn how to use Firebug debugger in Exercise 3 below.

6. Click the hyper link in the Error Console.  (Figure-1.43 below)


Figure-1.43: Error Console displays an error condition it found

7. There will be a new window that shows the line that contains the incorrect JavaScript code. (Figure-1.46 below)


Figure-1.46: The source of the error is displayed

8. Implant a few of your own syntactically incorrect JavaScript code and see if Error Console detects them.
9. Fix the incorrect code so that things would work again.


                                                                                                                    return to top of the exercise

Summary


In this exercise, you have built and run the "Data Validation" sample application that come with NetBeans IDE.  You have learned how XMLHttpRequest JavaScript object is created and used by the browser for performing an asynchronous communication with the server.  You also have learned how to use alert messaging and  Error Console for debugging your JavaScript code.

                                                                                                                        return to the top




Exercise 2: Use "Venkman" JavaScript Debugger and DOM Inspector - Deprecated


This exercise is deprecated since the "Firebug" JavaScript Debugger you are going to try in Exercise 3 provides the similar features of the "Venkman" and DOM Inspector.   If you still wants to try the exercise for some reason, please contact Sang Shin.


Exercise 3: Use "Firebug" Debugger


The Firebug is considered as the most useful JavaScript debugging tool among the ones available at the moment.  The following is the feature list quoted from the Firebug website.
In this exercise, you will explore these features of Firebug debugger using the "Data Validation with AJAX" project you built in Exercise 1.
  1. Watch and listen Firebug tutorial
  2. Open and enable Firebug debugger
  3. Spy on XMLHttpRequest traffic
  4. Use DOM Inspector of the Firebug debugger to analyze the document
  5. Log messages from JavaScript in your web page to the console (bye bye "alert debugging")
  6. JavaScript debugger for stepping through code one line at a time
  7. Use HTTP POST method (instead of HTTP GET method)


(3.0) Watch and listen Firebug tutorial

1. If you have an internet connection, please watch and listen Firebug tutorial.  This gives you an excellent tutorial on various features of Firebug debugger.
2. Please also read "An In-depth Look At The Future of JavaScript Debugging With Firebug" article
3. Do your own experimentation using the debugger.


(3.1) Open and enable Firebug debugger


Note: This section is updated with Firebug debugger of 1.0.1 as of Feb. 25th, 2007.

0. Start NetBeans IDE (if you have not done so yet.)
1. Right-click ajax-validation project and select Run in the NetBeans IDE.  The Firefox browser will get displayed.
2. Within the Firefox browser, select Tools from the menu-bar and then select Firebug->Open Firebug (Figure-3.10 below) or Firebug->Open Firebug in New Window.   Pressing F12 key also opens Firebug debugger.


Figure-3.10: Open Firebug debugger

                                                                                                                    return to top of the exercise


(3.2) Spy on XMLHttpRequest traffic


The Spy on XMLHttpRequest traffic feature lets you see the contents of the data being exchanged between the browser and the server.  This is a very useful debugging too.

1. Click Console tab of the Firebug debugger (if it has not been selected already)
2. Type in a couple of characters in the User Id field in the browser.  Notice that Firebug captures the traffic under Console tab window in the bottom part of the FireFox browser.
3. Expand the Get http://localhost:8080/ajax-validation/validate?id=xs and see the data that is returned from the server.  You should see <valid>true</valid> messages from the server. (Figure-3.21 below) 


Figure-3.21: Captured data from the server

                                                                                                                    return to top of the exercise


(3.3) DOM inspection


1. Click HTML tab of the Firebug debugger. (Figure-3.31 below)
2. Expand <body>, <form>, <table>, <tbody>, <tr>, 3rd <td> node.
3. Move your cursor to the <div> line.  You will notice that Firebug highlights the "Valid User Id" message by changing the background color in the browser. (Figure-3.31 below)


Figure-3.31: Start DOM Inspector of the Firebug

4. Move your mouse around and see how Firebug highlights the corresponding item in the page.

                                                                                                                    return to top of the exercise



(3.4) Log messages from JavaScript in your web page to the console (bye bye "alert debugging")


1. Within NetBeans, modify the index.jsp to insert several "console.log()" as shown below in Code-3.41.   Basically you are replacing the "alert()" you have inserted in Step 1.3 above with "console.log()".  Please note that "console.log()" uses a familiar printf() syntax for passing parameters.

<%-- Copyright 2005 Sun Microsystems, Inc. All rights reserved. You may not modify, use, reproduce, or distribute this software except in compliance with the terms of the License at: http://developer.sun.com/berkeley_license.html
$Id: index.jsp,v 1.4 2005/06/15 05:39:43 gmurray71 Exp $ --%>
<html>
    <head>

        <!-- JavaScript code starts from here -->
        <script type="text/javascript">
            var req;
            var target;
            var isIE;

            // (3) JavaScript function in which XMLHttpRequest JavaScript object is created.
            // Please note that depending on a browser type, you create
            // XMLHttpRequest JavaScript object differently.  Also the "url" parameter is not
            // used in this code (just in case you are wondering why it is
            // passed as a parameter).
            //
            function initRequest(url) {
                console.log("initRequest(%s) is called", url);
                if (window.XMLHttpRequest) {
                    req = new XMLHttpRequest();
                } else if (window.ActiveXObject) {
                    isIE = true;
                    req = new ActiveXObject("Microsoft.XMLHTTP");
                }
            }

            // (2) Event handler that gets invoked whenever a user types a character
            // in the input form field whose id is set as "userid".  This event
            // handler invokes "initRequest(url)" function above to create XMLHttpRequest
            // JavaScript object.
            //
            function validateUserId() {
                console.log("validateUserId() is called");
                if (!target) target = document.getElementById("userid");
                var url = "validate?id=" + escape(target.value);
  
                // Invoke initRequest(url) to create XMLHttpRequest object
                initRequest(url);
   
                // The "processRequest" function is set as a callback function.
                // (Please note that, in JavaScript, functions are first-class objects: they
                // can be passed around as objects.  This is different from the way
                // methods are treated in Java programming language.)
                req.onreadystatechange = processRequest;
                req.open("GET", url, true);
                req.send(null);
            }

            // (4) Callback function that gets invoked asynchronously by the browser
            // when the data has been successfully returned from the server.
            // (Actually this callback function gets called every time the value
            // of "readyState" field of the XMLHttpRequest object gets changed.)
            // This callback function needs to be set to the "onreadystatechange"
            // field of the XMLHttpRequest.
            //
            function processRequest() {
               console.log("processRequest() is called, req.readyState = %d", req.readyState);
               console.log("                                             req.responseXML = %s, req.responseText = %s", req.responseXML, req.responseText);
                if (req.readyState == 4) {
                  if (req.status == 200) {
       
                    // Extract "true" or "false" from the returned data from the server.
                    // The req.responseXML should contain either <valid>true</valid> or <valid>false</valid>
                    var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
           
                    // Call "setMessageUsingDOM(message)" function to display
                    // "Valid User Id" or "Invalid User Id" message.
                    setMessageUsingDOM(message);
           
                    // If the user entered value is not valid, do not allow the user to
                    // click submit button.
                    var submitBtn = document.getElementById("submit_btn");
                    if (message == "false") {
                        submitBtn.disabled = true;
                    } else {
                        submitBtn.disabled = false;
                    }
                  }
                }
            }

            // This function is not used for now. You will use this later.
            //
            function setMessageUsingInline(message) {
                mdiv = document.getElementById("userIdMessage");
                if (message == "false") {
                    mdiv.innerHTML = "<div style=\"color:red\">Invalid User Id</div>";
                } else {
                    mdiv.innerHTML = "<div style=\"color:green\">Valid User Id</div>";
                } 
            }

            // (5) Function in which message indicating the validity of the data gets displayed
            // through the "userIdMessage" <div> element.
            //
            function setMessageUsingDOM(message) {
               console.log("setMessageUsingDOM(%s) is called", message);
                var userMessageElement = document.getElementById("userIdMessage");
                var messageText;
                if (message == "false") {
                    userMessageElement.style.color = "red";
                    messageText = "Invalid User Id";
                } else {
                    userMessageElement.style.color = "green";
                    messageText = "Valid User Id";
                }
                var messageBody = document.createTextNode(messageText);
                // if the messageBody element has been created simple replace it otherwise
                // append the new element
                if (userMessageElement.childNodes[0]) {
                    userMessageElement.replaceChild(messageBody, userMessageElement.childNodes[0]);
                } else {
                    userMessageElement.appendChild(messageBody);
                }
            }

            function disableSubmitBtn() {
                var submitBtn = document.getElementById("submit_btn");
                submitBtn.disabled = true;
            }
        </script>
        <title>Form Data Validation using AJAX</title>
    </head>
    <body onload="disableSubmitBtn()">
 
        <h1>Form Data Validation using AJAX</h1>
        <hr/>
        <p>
            This example shows how you can use AJAX to do server-side form data validation without
            a page reload.
        </p>
        <p>
            In the form below enter a user id. By default the user ids &quot;greg&quot; and &quot;duke&quot;
            are taken. If you attempt to enter a user id that has been taken an error message will be
            displayed next to the form field and the &quot;Create Account&quot; button will be
            disabled. After entering a valid user id and selecting the &quot;Create Account&quot;
            button that user id  will be added to the list of user ids that are taken.
        </p>
 
        <form name="updateAccount" action="validate" method="post">
            <input type="hidden" name="action" value="create"/>
            <table border="0" cellpadding="5" cellspacing="0">
                <tr>
                    <td><b>User Id:</b></td>
                    <td>
                        <!-- (1) Input form field whose id is set as "userid" and "validateUserId()" function is
                        associated with the onkeyup event -->
                        <input    type="text"
                        size="20" 
                        id="userid"
                        name="id"
                        onkeyup="validateUserId()">
                    </td>
                    <!-- The "userIdMessage" div element specifies the location where input validation
                    message gets displayed. -->
                    <td>
                        <div id="userIdMessage"></div>
                    </td>
                </tr>
                <tr>
                    <td align="right" colspan="2">
                        <input id="submit_btn" type="Submit" value="Create Account">
                    </td>
                    <td></td>
                </tr>
            </table>
        </form>
    </body>
</html>
Code-3.41: Use console.log calls

2. Right-click ajax-validation project and select Run in the NetBeans IDE.  The Firefox browser will get displayed.
3. Type in a character into the User Id field in the browser.  
4. If the Firebug debugger is not opened, press F12 key.
5. If the Console tab has not been selected already, click Console tab. This will result in the display of debugging messages in the Firebug Console. (Figure-3.42 below).


Figure-3.42: Debugging messages are displayed in the Firebug Console.


                                                                                                                    return to top of the exercise


(3.5) JavaScript debugger for stepping through code one line at a time


1. Within the Firefox browser (not NetBeans IDE), click Script tab window.  The JavaScript code of the index.jsp is displayed.
2. Click in the left bar on the line you want to make a breakpoint - line number 44 in this example. (Figure-3.51 below)


Figure-3.51: Make a breakpoint

2. Type in a character into User Id field in the browser. The break point should be bit. Note the value of url variable in the right side pane of the debugger. (Figure-3.52 below)


Figure-3.52: Breakpoint is hit

Trouble-shooting: If somehow your breakpoint does not get hit, close the Firefox browser and run the project again so that the browser gets started by the NetBeans IDE or click refresh button of the browser.  Or you can click the Refresh icon of the Firefox browser (the icon with two arrows circling each other.)

3. Expand this in the Debugger pane. (Figure-3.53 below)
4. Expand req in the Debugger pane.  (Figure-3.53 below) The req variable represents XMLHttpRequest JavaScript object.  Note the values of various fields of the XMLHttpRequest such as onreadystatechange, readyState, responseXML, and responseText.


Figure-3.53: Expand nodes

5. Click Step Over icon or press F10 key twice to perform "Step Over" twice.  (Figure-3.54 below)


Figure-3.54: Perform Step Over

6. Observe that the onreadystatechange field is initialized.  (Figure-3.55 below)


Figure-3.55: Step over twice

7. Press Continue icon or press F8 to perform "Continue" operation.  (Figure-3.56 below)


Figure-3.56: Continue


Trouble-shooting:  If you make a breakpoint within the processRequest() function, you will notice that the req variable is not available in the right pane of the Inspector.  This is because processRequest() function is running in another thread and Firebug at this point does not know  how to display the req variable which was defined previously in a thread that is different from the one that runs processRequest().  In order to bypass this problem and for the sake of exercise, you can change the processRequest() as shown below in Code-3.55 and then make a breakpoint.  You will see duplicatereq variable is visible.

function processRequest() {
    var duplicatereq = req;
    if (req.readyState == 4) {
        if (req.status == 200) {
 
            // Extract "true" or "false" from the returned data from the server.
            var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            // Call "setMessageUsingDOM(message)" function to display
            // "Valid User Id" or "Invalid User Id" message.
            setMessageUsingDOM(message);
            // If the user entered value is not valid, do not allow the user to
            // click submit button.
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
        }
    }
}
Code-3.55: Make a duplicate variable inside the processRequest() function


                                                                                                                    return to top of the exercise


(3.6) Use HTTP POST method (instead of HTTP GET method)


In this step, you are going to change the ajax-validation project to use HTTP POST method instead of HTTP GET method.  Both client and server side code needs to be changed.

1. Within the NetBeans IDE, modify the validateUserId() method of the index.jsp to use HTTP POST instead of HTTP GET as shown in Code-3.60 below.  The new code fragments that need to be added are highlighted in bold and blue-colored font while the old code fragments that need to be removed or commented out are highlighted in bold and red colored font

function validateUserId() {
    if (!target) target = document.getElementById("userid");
    //var url = "validate?id=" + escape(target.value); 
    var url = "validate";
    var postdata = "id=" + escape(target.value);

    // Invoke initRequest(url) to create XMLHttpRequest object
    initRequest(url);

    // The "processRequest" function is set as a callback function.
    req.onreadystatechange = processRequest;
    //req.open("GET", url, true);
    //req.send(null);
    req.open("POST", url, true);
    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    req.send(postdata);
}
Code-3.60: Use HTTP POST

2. Modify the doPost() method of the ValidationServlet.java under ajax-validation->Source Packages->com.sun.j2ee.blueprints.bpcatalog.ajax as shown in Code-3.61 below.  The new code fragments that need to be added are highlighted in bold and blue-colored font while the old code fragments that need to be removed are highlighted in bold and red colored font.

    public  void doPost(HttpServletRequest request, HttpServletResponse  response)
        throws IOException, ServletException {

        /*
        String targetId = request.getParameter("id");
        if ((targetId != null) && !accounts.containsKey(targetId.trim())) {
            accounts.put(targetId.trim(), "account data");
            request.setAttribute("targetId", targetId);
            context.getRequestDispatcher("/success.jsp").forward(request, response);
        } else {
            context.getRequestDispatcher("/error.jsp").forward(request, response);
        }
        */
        doGet(request, response);
    }
Code-3.61: Modified ValidationServlet.java

3. Right click ajax-validation project and select Run.
4. If Firebug debugger is opened, press F12 key to open it.
4. Enter characters in the User Id field in the browser.
5. Expand POST http://localhost:8080/ajax-validation/validate line.  You should see something like Figure-3.62 below.


Figure-3.62: Captured data in HTTP POST request

6. Click Response tab in the Firebug Console.  You should see something like Figure-3.63 below.


Figure-3.63: Response from the server

                                                                                                                    return to top of the exercise

Summary


In this exercise, you have exercised various features of "Firebug" debugger.  You also learned how to use HTTP POST method instead of HTTP GET method.

                                                                                                                    return to the top



Exercise 4: Build and run other AJAX sample applications that come with NetBeans

In this exercise, you will build and run two other AJAX sample applications that come with NetBeans. The goal of the exercise to get you exposed other use cases of AJAX.


(4.1) Open, build, and run "Auto-completion with AJAX" sample application (named as "ajax-autocomplete" NetBeans project)

1. Open ajax-autocomplete NetBeans project.

2. Right-click ajax-autocomplete project and select Run. You should see the following. (Figure-4.11 below)


Figure-4.11: Result of running Auto-complete with AJAX sample application

                                                                                                                    return to top of the exercise


(4.2) Open, build, and run "Progress-bar with AJAX" sample application (named as "progress-bar" NetBeans project)


1. Open progress-bar NetBeans project.
2. Right-click progress-bar  project and select Run.  You should see the following. (Figure-4.21 below)


Figure-4.21: Result of running Progress-bar with AJAX sample application

3. Take a look at the source codes as you have done in Exercise 1. 

                                                                                                                    return to top of the exercise

Summary


In this exercise, you have built two other sample AJAX applications, Auto-complete and Progress bar.  These two sample applications demonstrate the usage of AJAX.

                                                                                                                          return to the top





Exercise 5: Use InnerHTML (instead of tedious DOM APIs) for setting inline contents


In this exercise, you are going to use InnerHTML (instead of tedious DOM APIs) for setting inline contents of an element.  For more information on innerHTML, please see here.
  1. Modify setMessageUsingDOM() function to use innerHTML
  2. Modify processRequest() to use setMessageUsingInline(message) function

(5.1) Modify setMessageUsingDOM() function to use innerHTML


1.  Double-click index.jsp under ajax-validation->Web Pages to open it in the source editor of NetBeans IDE and modify setMessageUsingDOM(message) function as shown in Code-5.10 below.   The code fragments that need to be removed or commented out are highlighted in bold and red-colored font. The code fragments that need to be added are highlighted in bold and blue-colored font.

function setMessageUsingDOM(message) {
     var userMessageElement = document.getElementById("userIdMessage");
     var messageText;
     if (message == "false") {
         userMessageElement.style.color = "red";
         messageText = "Invalid User Id";
     } else {
         userMessageElement.style.color = "green";
         messageText = "Valid User Id";
     }
     /*
     var messageBody = document.createTextNode(messageText);
     // if the messageBody element has been created simple replace it otherwise
     // append the new element
     if (userMessageElement.childNodes[0]) {
         userMessageElement.replaceChild(messageBody, userMessageElement.childNodes[0]);
     } else {
         userMessageElement.appendChild(messageBody);
     }
     */
     userMessageElement.innerHTML = messageText;
 }
Code-5.10: Use innerHTML

2. Right-click ajax-validation project and select Clean and Build.
3. Right-click ajax-validation project and select Run.  You should experience the same behavior.

                                                                                                                    return to top of the exercise


(5.2) Modify processRequest() to use setMessageUsingInline(message) function


1. Double-click index.jsp under ajax-validation->Web Pages to open it in the source editor of NetBeans IDE and modify processRequest() function to use setMessageUsingInline(message) function as shown in Code-5.11 below. 

function processRequest() {
    if (req.readyState == 4) {
        if (req.status == 200) {
            var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            // setMessageUsingDOM(message);
            setMessageUsingInline(message);
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
        }
    }
}

// This function uses innerHTML for setting inline contents
function setMessageUsingInline(message) {
    mdiv = document.getElementById("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style=\"color:red\">Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style=\"color:green\">Valid User Id</div>";
    } 
}
Code-5.20: Use setMessageUsingInline

2. Right-click ajax-validation project and select Clean and Build.
3. Right-click ajax-validation project and select Run.  You should experience the same behavior.

                                                                                                                    return to top of the exercise


Summary


In this exercise, you have learned how to use innerHTML for setting inline contents of an element.

                                                                                                                              return to the top




Homework Exercise (for people who are taking Sang Shin's "AJAX online course") - 2 hours


1. The homework is to modify the ajax-validation project to build your own HelloWorld AJAX application.  This application should behave as following.  (You might want to create a new project by copying the ajax-validation project.  You can name the homework project in any way you want but here I am going to call it ajax-hello.  Trouble-shooting: When you run ajax-hello project and the IDE complains that ajax-validation project already exists, please change the Context Root of the sun-web.xml file, which you find under WEB-INF directory, to ajax-hello or undeploy any existing ajax-hello project. )
2. Send the following files to ajaxhomework@sun.com with Subject as AJAXHomework-ajaxbasics2.
Or you can install Project Packager NetBeans plug-in (from http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=3742).  You can then export your project as a zip file - the build and dist directories are removed automatically to make the smaller zip file. If you set up SMTP, you can even e-mail your zipped project to someone.

                                                                                                                    return to the top