Using Dojo Toolkit for Building AJAX Applications (Remoting & Event Model & Usage of Widgets)

Sang Shin, sang.shin@sun.com, Sun Microsystems, www.javapassion.com/ajaxcodecamp



This hands-on lab takes you through the basics of using Dojo Toolkit for developing responsive and interactive AJAX-based web applications. This document is designed to get you going as quickly as possible. For more detail information on Dojo Toolkit, see the Resources section of Dojo Toolkit

The technologies you will get exposed in this hands-on lab include (1) how to use dojo.io.bind() call to do the remoting (2) how to use dojo.event.connect() call for handling various types of events including function calls (3) how to use the widgets that come with the Dojo toolkit (4) how to use JSON data format (5) how to retrieve data from the server.  

Creation of Dojo toolkit widgets will be dealt with in another hands-on lab.

Expected duration: 120 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.  The Dojo Toolkit 0.4.3 is already included in the hands-on lab zip file so there is no need for you to download it yourself.


OS platforms you can use


Change Log


Things to be done (by Sang Shin)


Lab Exercises



Exercise 1: Modify "Data Validation with AJAX" (ajax-validation) sample application to use dojo.io.bind()

In this exercise, you are going to modify "ajax-validation" sample program to use dojo.io.bind() call.  You will also use DOM manipulation utilities of Dojo toolkit as well. This sample application that you are going to modify is provided as ready-to-open NetBeans project called ajax-validation as part of the hands-on lab zip file (4260_ajaxdojointro.zip).

  1. Create and run a NetBeans project called ajax-validation from the "Data validation with AJAX" sample application
  2. Create a new NetBeans project by copying the ajax-validation project you created above
  3. Copy Dojo toolkit  JavaScript files into the new project
  4. Modify the  index.jsp of the new project to use dojo.io.bind(), build and run it
  5. Use DOM manipulation routines of Dojo toolkit
  6. Simulate error handling
  7. Experiment with mimetype
  8. Use formNode
  9. "Forward"/"Backward" and bookmarking
  10. Move JavaScript code into a separate file
  11. Modify "Auto-completion with AJAX" sample application to use dojo.io.bind()
Solution: The solution to this exercise is provided as a ready-to-open-and-run NetBeans project as part of hands-on lab zip file. You can find it under <LAB_UNZIPPED_DIRECTORY>/ajaxdojointro/solutions/ajax-validation-dojo.io.bind.solution.  You can just open it and run it.  If you are experiencing a problem and the problem is not obvious to find while doing this exercise, please open and run the ajax-validation-dojo.io.bind.solution project and see how it behaves.  

(1.1) Open, build, and run "ajax-validation" sample application

0. Start NetBeans IDE. 
1. Open ajax-validation NetBeans project. 

2. Build and run ajax-validation project.


Figure-1.12: Run the project

3.  Observe that the Firefox browser gets displayed. 
4. Type some characters like sa in the User Id: field.
5. Observe that Valid User Id string gets displayed on the right side of the text field. (Figure-1.13 below)  This is an example of AJAX usage case in which input data is being validated by the server through AJAX.

Data Validation using AJAX
Figure-1.13: Running "Data Validation using AJAX" sample application

                                                                                                                                        return to top of the exercise

(1.2) Create a new project by copying the ajax-validation project you created above

You can modify the ajax-validation project directly.  But just to preserve the original project in its current form, we will create a new project by copying the ajax-validation project into a new project.  You will name the new project as ajax-validation-dojo.io.bind.

1. Right click ajax-validation project node and select Copy Project.  The Copy Project dialog box appears.
2. In the Project Name: field, type in ajax-validation-dojo.io.bind. Click Copy button. (Figure-1.14 below)  The ajax-validation-dojo.io.bind project appears under Projects tab window.


Figure-1.14: Copy Project dialog box

3. Change Context path of the new project. (You have to do this only for NetBeans 5.0. It will be done automatically for NetBeans 5.5.1)


Figure-1.15: Change the context path to reflect the new project's context path

                                                                                                                                        return to top of the exercise

(1.3) Copy Dojo toolkit JavaScript files into the newly created project


Now you need to copy Dojo toolkit JavaScript library files into your Web application project.  Since the JavaScript files should reside in the same location as JSP, HTML files or image files, the library files needs to be copied into root directory (or sub-directory of the root) of your Web application structure.

Note: If you are using NetBeans 6.0, you can copy the files directly from the file system of the OS to the NetBeans project instead of having a directory to be added to the Favorite window as described below.

1. Right click on the ajax-validation-dojo.io.bind project.
2. In the Project Properties window, select the "source" option.
3. Click "Browse" button for the "Web Pages Folder" on the right hand pane of the project properties window.
4. Copy the contents inside the "File Name". This gives the location of the web folder where the Dojo toolkit JavaScript library files needs to be copied.
5. Open Windows explorer and go the folder selected above (e.g. C:\Sang-Ajaxcourse\ajax-validation-dojo.io.bind\web).
6. Copy the whole folder for dojo-release-1.0.1 to the ...\ajax-validation-dojo.io.bind\web.




Figure-1.16: Copy Web Folder Path

7. The netbeans project will automatically refresh and show the new Inside the netbeans project.

Folder update inside netbeans project
Figure-1.17: NetBeans Project automatically refreshes and shows the copied folder

                                                                                                                                        return to top of the exercise


(1.4) Modify index.jsp file to use dojo.io.bind()

In this step, you are going to modify index.jsp file to use dojo.io.bind() instead of directly creating XMlHttpRequest JavaScript object and using it to perform asynchronous communication with the server.

1. Double-click index.jsp of the ajax-validation-dojo.io.bind project to open it in the source editor window. (Figure-1.18 below) 


Figure-1.18: Open index.jsp in the source editor


2. Modify the index.jsp file as following.  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.  The code fragments that remain the same are in regular font.  Please pay attention to the comments that explain why some code fragments are removed and why some are added.  (This is the complete code of the index.jsp file.  So you can copy and paste the complete code.)

In dojo 1.0.x, dojo.io.bind() (used in versions prior to dojo 0.9) has been replaced with more specific functions that give a better indication about which IO transport is being used. However, the style of using one object argument that has properties detailing the IO call is still used. The new xhr functions are now:

 

<html>
<head>

<!-- Include Dojo toolkit library file dojo.js, make sure the directory path is set correctly -->
<script language="JavaScript"
            type="text/javascript"
            src="dojo-release-1.0.1/dojo/dojo.js">
</script>
   

<script type="text/javascript">
//var req;   // No longer needed since we don't need to deal with XMLHttpRequest
var target;
//var isIE;   // No longer needed since wd don't need to deal with browser difference

/********************* Commented out *******************
  * We don't need to do the low-level XMLHttpRequest handling anymore
  * since it is handled by dojo.io.bind().

function initRequest(url) {
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        isIE = true;
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

function validateUserId() {
    if (!target) target = document.getElementById("userid");
    var url = "validate?id=" + escape(target.value);   
    initRequest(url);
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);
}
******************************************************/

// This is the modified validateUserId() function
function validateUserId() {
   if (!target) target = document.getElementById("userid");
   var sampleFormNode = document.getElementById("submit_btn");

   // Use dojo.xhrGet() for remoting
   dojo.xhrGet( {
        // URL - location of the data you want to get
        url: "validate?id=" + escape(target.value),
        handleAs: "xml",
        timeout: 5000, // Time in milliseconds

        load: function(response, ioArgs) {
           processRequest(response);
        },

        // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {
           alert("dojo Error alert !!");
        },
    });

}


/********************* commented out **************************************
  * We don't need to check the status and readyState anymore
since it is handled by dojo.io.bind()
function processRequest() {
    if (req.readyState == 4) {
        if (req.status == 200) {
            var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            setMessageUsingDOM(message);
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
        }
    }
}
***********************************************************************/

// This is the modified processRequest() function.  Note that you don't need to
// check if the readyState of the XMLHttpRequest is 4 or not.
function processRequest(data) {
            var message = data.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
            setMessageUsingDOM(message);
            var submitBtn = document.getElementById("submit_btn");
            if (message == "false") {
              submitBtn.disabled = true;
            } else {
              submitBtn.disabled = false;
            }
}

function setMessageUsingInline(message) {
    mdiv = document.getElementById("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style='color:red; font-family:Arial; font-size:14px; font-weight:bold;'>Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style='color:green; font-family:Arial; font-size:14px; font-weight:bold;'>Valid User Id</div>";
    } 
}

 function setMessageUsingDOM(message) {
     var userMessageElement = document.getElementById("userIdMessage");
     var messageText;
     if (message == "false") {
          userMessageElement.style.color = "red";
          userMessageElement.style.fontFamily = "Arial";
          userMessageElement.style.fontSize = "14px";
          userMessageElement.style.fontWeight = "bold";
          messageText = "Invalid User Id";
     } else {
          userMessageElement.style.color = "green";
          userMessageElement.style.fontFamily = "Arial";
          userMessageElement.style.fontSize = "14px";
          userMessageElement.style.fontWeight = "bold";
          messageText = "Valid User Id";
     }

     /***********   commented out ******************
     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);
     }
     *******************************************/
      // Use innerHTML instead of raw DOM API
      userMessageElement.innerHTML = messageText;
  }

   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><font face="Arial" size="2">
                This example shows how you can use AJAX to do server-side form data validation without
                a page reload.
                <br><br>
                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.
        </font></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 type="text"
                                   size="20" 
                                   id="userid"
                                   name="id"
                                   onkeyup="validateUserId()">
                    </td>
                    <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>

3. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  The new project should behave in the same way ajax-validation project behaved as you've seen above

Trouble-shooting:  If you are experiencing a problem, for example, typing a string in the User Id: field does not generate Valid User Id message (Figure-1.42 below), open JavaScript Console and see the debugging messages displayed (Figure-1.20 below).

Firebug Console
Figure-1.19: Open Firebug Console for debugging


Firebug Console error display
Figure-1.20: Firebug Console displays errors discovered in the JavaScript.  The above indicates that you have not copied dojo.js file into your project yet or copied it into a directory that is not specified correctly in the index.jsp page.

                                                                                                                                        return to top of the exercise

(1.5) Use DOM manipulation methods of Dojo toolkit


In this step, you will use some DOM manipulation methods from Dojo toolkit.

1.  Modify the index.jsp file as shown in Code-1.50 below.  Please note that code in the Code-1.50 shows code only between <script> and </script>.  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.  The code fragments that remain the same are in regular font.  Please pay attention to the comments that explain why some code fragments are removed and why some are added.
In dojo release 1.0.X, the dom functionality is no more part of the core package and has moved to dojox.data.dom package. Hence it is required to include the line "dojo.require("dojox.data.dom");". While referencing the dom functionality it is required to use: var message = dojox.data.dom.textContent(....)

<!-- Include Dojo toolkit library file dojo.js, make sure the directory path is set correctly -->
<script language="JavaScript"
        type="text/javascript"
        src="dojo-release-1.0.1/dojo/dojo.js">
</script>
  

<script type="text/javascript">
dojo.require("dojox.data.dom");
var target;


// This is the modified validateUserId() function
function validateUserId() {
    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");
    //var sampleFormNode = document.getElementById("submit_btn");
    var sampleFormNode = dojo.byId("submit_btn");
   
    // Use dojo.xhrGet() for remoting
    dojo.xhrGet( {
        // URL -  location of the data you want to get
        url: "validate?id=" + escape(target.value),
        handleAs: "xml",
        timeout: 5000, // Time in milliseconds

        load: function(response, ioArgs) {
            processRequest(response);
        },

        // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {
            alert("dojo Error alert !!");
        },
     });
}

// This is the modified processRequest() function
function processRequest(data) {
       // var message = data.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
       var message = dojox.data.dom.textContent(data.getElementsByTagName("valid")[0]);
       setMessageUsingDOM(message);
       //var submitBtn = document.getElementById("submit_btn");
       var submitBtn = dojo.byId("submit_btn");
       setMessageUsingDOM(message);
       if (message == "false") {
           submitBtn.disabled = true;
       } else {
           submitBtn.disabled = false;
       }
}

function setMessageUsingInline(message) {
    //mdiv = document.getElementById("userIdMessage");
    mdiv = dojo.byId("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 setMessageUsingDOM(message) {
     //var userMessageElement = document.getElementById("userIdMessage");
     var userMessageElement = dojo.byId("userIdMessage");
     var messageText;
     if (message == "false") {
         userMessageElement.style.color = "red";
         messageText = "Invalid User Id";
     } else {
         userMessageElement.style.color = "green";
         messageText = "Valid User Id";
     }
     userMessageElement.innerHTML = messageText;
 }

function disableSubmitBtn() {
    //var submitBtn = document.getElementById("submit_btn");
    var submitBtn = dojo.byId("submit_btn");
    submitBtn.disabled = true;
}
</script>

Code-1.50: Use DOM Utility methods from Dojo toolkit

2. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  The new project should behave in the same way ajax-validation project behaved as you've seen above.

                                                                                                                                        return to top of the exercise

(1.6) Error handling


In this step, you are going to simulate a HTTP error condition and see if the error handler in the dojo.xhrGet() gets called.

1. Modify validateUserId() function of the index.jsp as shown in Code-1.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 are highlighted in bold and red colored font.  This is to generate a HTTP error condition by passing an invalid path.

// This is the modified validateUserId() function
function validateUserId() {
    //if (!target) target = document.getElementById("userid");
        if (!target) target = dojo.byId("userid");
        //var sampleFormNode = document.getElementById("submit_btn");
        var sampleFormNode = dojo.byId("submit_btn");

    dojo.xhrGet( {
        // URL -  location of the data you want to get
        //url: "validate?id=" + escape(target.value),
        url: "validateInvalidURL?id=" + escape(target.value),
        handleAs: "xml",
        timeout: 5000, // Time in milliseconds

        load: function(response, ioArgs) {
            processRequest(response);
        },

        // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {
            alert("dojo Error alert !!");
        },
    });
}
Code-1.60: Simulate an error condition by passing invalid URL

2. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  Firefox browser gets displayed.
3. Type in a character into the User Id field in the browser.
4. You should see an alert message. (Figure-1.61 below)


Figure-1.60: Error handler displays an alert message

5.  Remove the error condition by removing the change you made in validateUserId() function of the index.jsp in Code-1.60 above.

                                                                                                                                        return to top of the exercise

(1.7) Experiment with "mimetype"

Please note that you were using "text/xml" as a value of "mimetype".  What it does is when the data is received from the server, it is parsed as an XML document.  That is the reason you could extract the data as an DOM object inside the proceRequest(data) function as shown below. Since it is a DOM object you could use getElementByTagName(..) or dojo.dom.textContent(..) DOM methods.

// This is the modified processRequest() function. Note that you don't need to
// check if the readyState of the XMLHttpRequest is 4 or not.
function processRequest(data) {
    // var message = data.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
    var message = dojox.data.dom.textContent(data.getElementsByTagName("valid")[0]);
    setMessageUsingDOM(message);
    //var submitBtn = document.getElementById("submit_btn");
    var submitBtn = dojo.byId("submit_btn");
    setMessageUsingDOM(message);
    if (message == "false") {
        submitBtn.disabled = true;
    } else {
        submitBtn.disabled = false;
    }
}
Code-1.70: XML parsing of received data

Now let's change the mimetype to "text" and see what happens.

1. Modify validateUserId() function of the index.jsp as shown in Code-1.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 are highlighted in bold and red colored font

// This is the modified validateUserId() function
function validateUserId() {
    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");
        //var sampleFormNode = document.getElementById("submit_btn");
        var sampleFormNode = dojo.byId("submit_btn");         

    // Use dojo.xhrGet() for remoting
     dojo.xhrGet( {
         // URL - location of the data you want to get
        url: "validate?id=" + escape(target.value),
         //handleAs: "xml",
         handleAs: "text",
         timeout: 5000, // Time in milliseconds

        load: function(response, ioArgs) {
            processRequest(response);
        },     

        // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {
            alert("dojo Error alert !!");
        },
    });
}

Code-1.71: Simulate an error condition by passing invalid URL

2. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  Firefox browser gets displayed.
3. Press F12 key to see the FireBug debugger.
5. Type in characters into the User Id field in the browser.
6. Observe the error condition reported by the FireBug debugger. (Figure-1.72 below)  The error indicates that the data that is received from the server is not parsed as an XML document anymore since you indicated your intention of receiving the data in plain text form. 


Figure-1.72: The data is not interpreted as XML

7. Change it the mimetype to "text/xml"   to make things work again.

                                                                                                                                        return to top of the exercise

(1.8) Use "formNode" parameter of dojo.io.bind()


In this step, you are going to use "formNode" to send the entire form (collection of input form fields) to the server.

1. Modify validateUserId() function of the index.jsp as shown in Code-1.80 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

// This is the modified validateUserId() function
function validateUserId() {
    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");
    //var sampleFormNode = document.getElementById("submit_btn");
    var sampleFormNode = dojo.byId("submit_btn");

     // Use dojo.xhrGet() for remoting
     dojo.xhrGet( {
         // URL - location of the data you want to get
         //url: "validate?id=" + escape(target.value),
         // Since the form data is passed to the server via formNode
         // parameter below, no need to pass target.value here.
         url: "validate",

         // Send form data. "myform" is the id of the input form.
         form: dojo.byId("myform"),
         handleAs: "xml",
         timeout: 5000, // Time in milliseconds

         load: function(response, ioArgs) {
             processRequest(response);
         },

         // The ERROR function will be called in an error case.
         error: function(response, ioArgs) {
             alert("dojo Error alert !!");
         },
});
}

Code-1.80: Use formNode

2. Modify the <form ..> element to give it an id attribute as shown in Code-1.81 below.  Note that  id="myform" is added as an attribute.

<form id="myform" 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 type="text"
               size="20" 
               id="userid"
               name="id"
               onkeyup="validateUserId()">
        </td>
        <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>
Code-1.81: Add an id attribute to the <form ..> element

3. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  Firefox browser gets displayed.
4. Press F12 key to see the FireBug debugger.
5. Type in characters into the User Id field in the browser.
6. Observe the HTTP traffic between the browser and server. (Figure-1.82 below)


Figure-1.82: Usage of formNode

                                                                                                                                        return to top of the exercise

(1.9) Play around with "Forward"/"Backward" and bookmarking


1. Modify the index.jsp as shown in Code-1.90 below.  The new code fragments that need to be added are highlighted in bold and blue colored font.

    <head>
    <!-- Include Dojo toolkit library file dojo.js, make sure the directory path is set correctly -->
    <script language="JavaScript"
            type="text/javascript"
            src="dojo-release-1.0.1/dojo/dojo.js">
    </script>

    <script type="text/javascript">
    dojo.require("dojox.data.dom");
    dojo.require("dojo.back");
    djConfig = {
        //dojoIframeHistoryUrl: "dojo-1.0.0/dojo/resources/iframe_history.html", //for xdomain
        preventBackButtonFix: false
    };
    var state = {
        back: function() { alert("BackButton was pressed !"); },
        forward: function() { alert("Forward Button was pressed !"); },
    };
    dojo.back.init();
    dojo.back.setInitialState(state);
   
    //var req;   // No longer needed since we don't need to deal with XMLHttpRequest
    //var isIE;   // No longer needed since wd don't need to deal with browser difference
    var target;

    /********************* Commented out *******************
     * We don't need to do the low-level XMLHttpRequest handling anymore
     * since it is handled by dojo.io.bind().
    function initRequest(url) {
        if (window.XMLHttpRequest) {
            req = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            isIE = true;
            req = new ActiveXObject("Microsoft.XMLHTTP");
        }
    }

    function validateUserId() {
        if (!target) target = document.getElementById("userid");
        var url = "validate?id=" + escape(target.value);  
        initRequest(url);
        req.onreadystatechange = processRequest;
        req.open("GET", url, true);
        req.send(null);
    }
    ******************************************************/

   
    // This is the modified validateUserId() function
    function validateUserId() {
        dojo.back.addToHistory(state);

    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");
    //var sampleFormNode = document.getElementById("submit_btn");
    var sampleFormNode = dojo.byId("submit_btn");

    // Use dojo.xhrGet() for remoting
    dojo.xhrGet( {
        // URL -  location of the data you want to get
        //url: "validate?id=" + escape(target.value),
        // Since the form data is passed to the server via formNode
        // parameter below, no need to pass target.value here.
        url: "validate",  

        // Send form data. "myform" is the id of the input form.
        form: dojo.byId("myform"),   
        handleAs: "xml",
        timeout: 5000, // Time in milliseconds


        load: function(response, ioArgs) {
        processRequest(response);
        },

        // The ERROR function will be called in an error case.
        error: function(response, ioArgs) {
        alert("dojo Error alert !!");
        },
    });
    }
     

    /********************* commented out **************************************
     * We don't need to check the status and readyState anymore since it is handled by dojo.io.bind()
    function processRequest() {
        if (req.readyState == 4) {
            if (req.status == 200) {
                var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
                setMessageUsingDOM(message);
                var submitBtn = document.getElementById("submit_btn");
                if (message == "false") {
                  submitBtn.disabled = true;
                } else {
                  submitBtn.disabled = false;
                }
            }
        }
    }
    ***********************************************************************/

    // This is the modified processRequest() function.  Note that you don't need to
    // check if the readyState of the XMLHttpRequest is 4 or not.
    function processRequest(data) {
    // var message = data.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
    var message = dojox.data.dom.textContent(data.getElementsByTagName("valid")[0]);
    setMessageUsingDOM(message);
    //var submitBtn = document.getElementById("submit_btn");
    var submitBtn = dojo.byId("submit_btn");
    setMessageUsingDOM(message);
    if (message == "false") {
      submitBtn.disabled = true;
    } else {
      submitBtn.disabled = false;
    }
    }

    function setMessageUsingInline(message) {
    //mdiv = document.getElementById("userIdMessage");
        mdiv = dojo.byId("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style='color:red; font-family:Arial; font-size:14px; font-weight:bold;'>Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style='color:green; font-family:Arial; font-size:14px; font-weight:bold;'>Valid User Id</div>";
    } 
    }

    function setMessageUsingDOM(message) {
        //var userMessageElement = document.getElementById("userIdMessage");
        var userMessageElement = dojo.byId("userIdMessage");
        var messageText;
        if (message == "false") {
            userMessageElement.style.color = "red";
            userMessageElement.style.fontFamily = "Arial";
            userMessageElement.style.fontSize = "14px";
            userMessageElement.style.fontWeight = "bold";
            messageText = "Invalid User Id";
        } else {
            userMessageElement.style.color = "green";
            userMessageElement.style.fontFamily = "Arial";
            userMessageElement.style.fontSize = "14px";
            userMessageElement.style.fontWeight = "bold";
            messageText = "Valid User Id";
        }

    /*********** commented out ******************
    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);
    }
    *******************************************/
    // Use innerHTML instead of raw DOM API
    userMessageElement.innerHTML = messageText;
     }

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


Code-1.90: Add Dojo configuration

2. Verify that there is a file iframe_history.html in the directory dojo-release-1.0.1\dojo\resources.
3. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  Firefox browser gets displayed.
4. Press F12 key to see the FireBug debugger.
5. Type in characters into the User Id field in the browser.
6. Press backward button.  (Figure-1.91 below)
7. Observe the alert message. (Figure-1.91 below) Click OK button to close the alert message.


Figure-1.91: Backward button function gets called

9. Press forward button. (Figure-1.92 below)
10. Observe the alert message. (Figure-1.92 below)


Figure-1.92: Forward button function gets called

Now let's suppose that we want the behavior of the backward button to be something like "starting new". 

11.  Modify validateUserId() function of the index.jsp as shown in Code-1.93 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.

// This is the modified validateUserId() function
function validateUserId() {

    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");

    //var sampleFormNode = document.getElementById("submit_btn");
    var sampleFormNode = dojo.byId("submit_btn");
   
    // Use dojo.io.bind() for remoting
    dojo.io.bind({
          // URL -  location of the data you want to get
          //url: "validate?id=" + escape(target.value),
          // Since the form data is passed to the server via formNode
          // parameter below, no need to pass target.value here.
          url: "validate",   

          // The server code in this ajax-validation
          // sample application expects "GET".  It
          // uses "POST" for account creation. Probably
          // not a good practice but that is the way
          // the server side code is written for now.
          method: "GET", 

          // Send form data. "myform" is the id of the input form.
 
          formNode: dojo.byId("myform"),    

         
          // Callback function that you'd like to have called
          // when you actually do get the data
          load: function(type, data, evt){ processRequest(data);},
         
          // Error handling
          error: function(type, error){ alert("error"); },
         
          // Backward and forward button handling
          // backButton: function() { alert("back button pressed"); },
          // New backward button behavior
          backButton: function() {
                 // Blank out the User Id field value
                 target.value="";

                 // Display a message indicating Backward button is pressed
                 var userMessageElement = dojo.byId("userIdMessage");
                 userMessageElement.innerHTML = "Backward button pressed. Start again!";

                 // Disable the submit button
                 var submitBtn = dojo.byId("submit_btn");
                 submitBtn.disabled = true;
          },
          forwardButton: function() { alert("forward button pressed");},
         
          // Type of data you want to receive
          mimetype: "text/xml"         
    }); 
}
Code-1.93: Modified behavior of the backward button

12. Right-click ajax-validation-dojo.bo.bind project and select Run Project.  Firefox browser gets displayed.
13. Type in characters into the User Id field in the browser.
14. Press backward button.  (Figure-1.91 below)
15. Observe that User Id field is blanked out while "Backward button pressed, Start again!" message gets displayed and the Created Account button is disabled.


Figure-1.94: New Backward button behavior


                                                                                                                                        return to top of the exercise

(1.10) Move JavaScript code into a separate file

In this step, you are going to move the JavaScript code into another file called, myjavascript.js.  This is an optional step.

1.  (Assuming you have installed NetBeans JavaScript editor plug-in), right click Web Pages under ajax-validation-dojo.io.bind project node and select New->JSTemplate.js.
2. Observe New JSTemplate.js dialog box appears.
3. For the File Name field, type in myjavascript. Click Finish. (Figure-1.100 below)


Figure-1.100: Create myjavascript.js file

Note: If you have not installed NetBeans JavaScript editor plug-in, just create a new file called myjavacript.js under Web Pages directory.

4. Move the code fragment shown in Code-1.101 below  from the index.jsp to the newly created myjavascript.js file.  You are basically moving JavaScript code into a separate file.  Note that you do not need to wrap the JavaScript code in the myjavascript.js file with <script..> and </script> tags.

dojo.require("dojox.data.dom");
dojo.require("dojo.back");
djConfig = {
    //dojoIframeHistoryUrl: "dojo-1.0.0/dojo/resources/iframe_history.html",
    preventBackButtonFix: false
};
var state = {
    back: function() {
    // Blank out the User Id field value
    target.value="";

    // Display a message indicating Backward button is pressed
    var userMessageElement = dojo.byId("userIdMessage");
    userMessageElement.innerHTML = "Backward button pressed. Start again!";

    // Disable the submit button
    var submitBtn = dojo.byId("submit_btn");
    submitBtn.disabled = true;
    },
    forward: function() { alert("Forward Button was pressed !"); },
};
dojo.back.init();
dojo.back.setInitialState(state);

//var req;   // No longer needed since we don't need to deal with XMLHttpRequest
//var isIE;   // No longer needed since wd don't need to deal with browser difference
var target;

/********************* Commented out *******************
 * We don't need to do the low-level XMLHttpRequest handling anymore
 * since it is handled by dojo.io.bind().
function initRequest(url) {
    if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
    isIE = true;
    req = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

function validateUserId() {
    if (!target) target = document.getElementById("userid");
    var url = "validate?id=" + escape(target.value);  
    initRequest(url);
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);
}
******************************************************/


// This is the modified validateUserId() function
function validateUserId() {
    dojo.back.addToHistory(state);

    //if (!target) target = document.getElementById("userid");
    if (!target) target = dojo.byId("userid");
    //var sampleFormNode = document.getElementById("submit_btn");
    var sampleFormNode = dojo.byId("submit_btn");

    // Use dojo.xhrGet() for remoting
    dojo.xhrGet( {
    // URL -  location of the data you want to get
    //url: "validate?id=" + escape(target.value),
    // Since the form data is passed to the server via formNode
    // parameter below, no need to pass target.value here.
    url: "validate",  

    // Send form data. "myform" is the id of the input form.
    form: dojo.byId("myform"),   
    handleAs: "xml",
    timeout: 5000, // Time in milliseconds


    load: function(response, ioArgs) {
        processRequest(response);
    },

    // The ERROR function will be called in an error case.
    error: function(response, ioArgs) {
        alert("dojo Error alert !!");
    },
    });
}


/********************* commented out **************************************
 * We don't need to check the status and readyState anymore since it is handled by dojo.io.bind()
function processRequest() {
    if (req.readyState == 4) {
    if (req.status == 200) {
        var message = req.responseXML.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
        setMessageUsingDOM(message);
        var submitBtn = document.getElementById("submit_btn");
        if (message == "false") {
          submitBtn.disabled = true;
        } else {
          submitBtn.disabled = false;
        }
    }
    }
}
***********************************************************************/

// This is the modified processRequest() function.  Note that you don't need to
// check if the readyState of the XMLHttpRequest is 4 or not.
function processRequest(data) {
    // var message = data.getElementsByTagName("valid")[0].childNodes[0].nodeValue;
    var message = dojox.data.dom.textContent(data.getElementsByTagName("valid")[0]);
    setMessageUsingDOM(message);
    //var submitBtn = document.getElementById("submit_btn");
    var submitBtn = dojo.byId("submit_btn");
    setMessageUsingDOM(message);
    if (message == "false") {
      submitBtn.disabled = true;
    } else {
      submitBtn.disabled = false;
    }
}

function setMessageUsingInline(message) {
    //mdiv = document.getElementById("userIdMessage");
    mdiv = dojo.byId("userIdMessage");
    if (message == "false") {
       mdiv.innerHTML = "<div style='color:red; font-family:Arial; font-size:14px; font-weight:bold;'>Invalid User Id</div>";
    } else {
       mdiv.innerHTML = "<div style='color:green; font-family:Arial; font-size:14px; font-weight:bold;'>Valid User Id</div>";
    } 
}

function setMessageUsingDOM(message) {
    //var userMessageElement = document.getElementById("userIdMessage");
    var userMessageElement = dojo.byId("userIdMessage");
    var messageText;
    if (message == "false") {
    userMessageElement.style.color = "red";
    userMessageElement.style.fontFamily = "Arial";
    userMessageElement.style.fontSize = "14px";
    userMessageElement.style.fontWeight = "bold";
    messageText = "Invalid User Id";
    } else {
    userMessageElement.style.color = "green";
    userMessageElement.style.fontFamily = "Arial";
    userMessageElement.style.fontSize = "14px";
    userMessageElement.style.fontWeight = "bold";
    messageText = "Valid User Id";
    }

    /*********** commented out ******************
    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);
    }
    *******************************************/
    // Use innerHTML instead of raw DOM API
    userMessageElement.innerHTML = messageText;
 }

function disableSubmitBtn() {
    //var submitBtn = document.getElementById("submit_btn");
    var submitBtn = dojo.byId("submit_btn");
    submitBtn.disabled = true;
}

Code-1.101: myjavascript.js

5. Modify index.jsp to include myjavascript.js as shown in Code-1.102 below.  

<%-- 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>
    <!-- Include Dojo toolkit library file dojo.js, make sure the directory path is set correctly -->
    <script language="JavaScript"
            type="text/javascript"
            src="dojo-release-1.0.1/dojo/dojo.js">
    </script>

    <script language="JavaScript"
        type="text/javascript"
        src="myjavascript.js">
    </script>
    <title>Form Data Validation using AJAX</title>
    </head>
    <body onload="disableSubmitBtn()">
       
        <h1>Form Data Validation using AJAX</h1>
        <hr/>
        <p><font face="Arial" size="2">
                This example shows how you can use AJAX to do server-side form data validation without
                a page reload.
                <br><br>
                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.
        </font></p>
       
    <form id="myform" 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 type="text"
                   size="20" 
                   id="userid"
                   name="id"
                   onkeyup="validateUserId()">
            </td>
            <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.102: index.jsp without JavaScript code

12. Right-click ajax-validation-dojo.bo.bind project and select Run Project.


                                                                                                                                        return to top of the exercise


Solution to Exercise 1


The solution to this exercise is provided in  <LAB_UNZIPPED_DIRECTORY>/ajaxdojointro/solutions/ajax-validation-dojo.io.bind.solution.  You can just open the project and run it.

Summary

In this exercise, you have modified sample applications that use XMLHttpRequest  to use dojo.io.bind() from Dojo toolkit instead. 

                                                                                                                                                return to the top



Exercise 2: Dojo Event Model

In this exercise, you will exercise the various event programming models of the Dojo toolkit.


(2.1) Create a new project called "dojo-event1" by copying the "ajax-validation-dojo.io.bind" project you created above

You can create a new NetBeans project from scratch but you can also create one that contains Dojo toolkit library file by copying the ajax-validation-dojo.io.bind project.  We will name it as dojo-event1 project.

1. Right click ajax-validation-dojo.io.bind project and select Copy Project.  The Copy Project dialog box appears.
2. In the Project Name: field, type in dojo-event1. (Figure-2.10 below) The copying project might take a couple of minutes. The dojo-event1 project appears under Projects tab window.


Figure-2.10: Create dojo-event1 project by copying

3. Change Context Path of the new project. (You have to do this only for NetBeans 5.0.  It is taken care of automatically for NetBeans 5.5)


Figure-2.11: Change the Context Path to /dojo-event1

                                                                                                                                        return to top of the exercise

(2.2) Use dojo.event.connect() call for a user initiated event

We are now going to modify the index.jsp in which we are going to use dojo.event.connect().  

Note: If you want to leave the original code intact, you can always create a new project by copying the current project.  Please make sure you change the Context Path: to reflect the new project name if you decided to do so.

1. Double-click the index.jsp file of the dojo-event1 project to open it in the  source editor.  Please make sure you do not change the index.jsp file of the ajax-validation-dojo.io.bind project unless that is what you want to.

2. Replace the complete content of the index.jsp file with the following code (Code-2.20 below).  Please study how dojo.event.connect() call is used to register myHandler event handler to the onclick property of the mylink node.

<html>
    <head>
    <!-- Include Dojo toolkit library file dojo.js, make sure the directory is correct -->
    <script language="JavaScript"
        type="text/javascript"
        src="dojo-release-1.0.1/dojo/dojo.js">
    </script>  

    <script type="text/javascript">
    window.onload = function () {
        var link = document.getElementById("mylink");

        // "myHandler" event handler is registered to the
       // "onclick" property of the "mylink" node.
       dojo.connect(link, "onclick", myHandler);
    }

    // Define an event handler named as "myHandler"
    function myHandler(evt) {
       alert("myHandler: invoked - this is a named event handler" +
               "\r\n" +
               "If you see this, that means calling dojo.connect() was successful.");
    }

    </script>
    <title>Simple Dojo Event Handler</title>
    </head>
    <body>
   
    <h1>Simple Dojo Event Handler</h1>
    <hr/>
    <p><font face="Arial" size="2">
        This example shows the usage of dojo.event.connect()
        registering an event handler to a DOM node. When a user clicks on
        "Click Me" link, it should pop up an alert, which is
        invoked within the event handler.
    </font></p>
   
    <a href="#" id="mylink">Click Me</a>
   
    </body>
</html>
          Code-2.20: New index.jsp to use dojo.event.connect() call

3. Right click dojo-event1 project and select Clean and build project.
4. Right click Rdojo-event1 project and select Run Project.  You should see the following page. (Figure-2.21 below)


Figure-2.21: Simple Dojo Event Handler front page

4. Click Click Me link.  You should see an alert message that gets invoked inside the event handler. (Figure-2.22 below) Click OK to close the alert.


Figure-2.22: Alert message from the event handler


(2.3) Use dojo.event.connect() call for window loading event

1. Modify the index.jsp to use dojo.connect() call for window loading event.  (Code-2.30 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.  The code fragments that remain the same are in regular font.

<html>
    <head>
    <!-- Include Dojo toolkit library file dojo.js, make sure the directory is correct -->
    <script language="JavaScript"
        type="text/javascript"
        src="dojo-release-1.0.1/dojo/dojo.js">
    </script>  
    <script type="text/javascript">
    //window.onload = function () {
    function myInit() {
        var link = document.getElementById("mylink");

        // "myHandler" event handler is registered to the
        // "onclick" property of the "mylink" node.
        dojo.connect(link, "onclick", myHandler);
    }

    // Use dojo.connect to register "myInit" function to "onload" event.
    dojo.connect(window, "onload", myInit);        

    // Define an event handler named as "myHandler"
    function myHandler(evt) {
        alert("myHandler: invoked - this is a named event handler" +
                "\r\n" +
                "If you see this, that means calling dojo.connect() was successful." +
                "\r\n" +
                "In this call we are using dojo.connect to register \"myInit\" function to \"onload\" event.");
    }

    </script>
    <title>Simple Dojo Event Handler</title>
    </head>
    <body>
       
        <h1>Simple Dojo Event Handler</h1>
        <hr/>