Book Image

Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained

Book Image

Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained

Overview of this book

Microsoft AJAX Library Essentials is a practical reference for the client-side library of the ASP.NET AJAX Framework 1.0, and a tutorial for the underlying technologies and techniques required to use the library at its full potential. The main goal of this book is to get you comfortable with the Microsoft AJAX Library, a huge set of functions that can be used for developing powerful client-side functionality.Beginning with a hands-on tour of the basic technologies associated with AJAX, JavaScript, XMLHttpRequest, JSON, and the DOM, you'll move on to a crash course in the Microsoft AJAX tools. You will learn, through numerous step-by-step exercises, how to create basic AJAX applications, how the object-based programming model of JavaScript works, and how Microsoft AJAX Library extends this model. You'll understand the architecture of the Microsoft AJAX components, how they all fit together, and exactly what they can do for you. Then you will learn how to use the Microsoft AJAX Library in your web projects, and a detailed case study will walk you through creating your own customized client components. At every stage of your journey, you'll be able to try out examples to illuminate the theory, and consolidate your understanding. In addition to learning about the client and server controls, you'll also see how to handle errors and debug your AJAX applications.To complement your new found skills, the book ends with a visual reference of the Microsoft AJAX Library namespaces and classes, including diagrams and quick explanations for all the classes mentioned in the book, providing an invaluable reference you will turn to again and again.
Table of Contents (14 chapters)
Copyright
Credits
About the Authors
About the Reviewers
Preface

Hello World!


ASP.NET AJAX is a powerful and exciting framework which comes with lot of built-in features, but in order to make the most out of it, it’s best to take a disciplined approach and learn the basics first. Chapter 2 and Chapter 3 will teach you the foundations, and how to implement basic AJAX features with ASP.NET without using the ASP.NET AJAX Framework. You’ll start using the Microsoft AJAX Library in Chapter 4.

This chapter ends with an exercise where we’ll build a simple AJAX application with ASP.NET, named Quickstart. This application doesn’t make use of any of the components of the Microsoft ASP.NET AJAX Framework. Instead, it uses simple JavaScript and C# code.

Note

Going through this exercise is optional. The exercise is for the most impatient readers willing to start coding as soon as possible, but it assumes you’re already familiar with JavaScript, ASP.NET, and XML. If this is not the case, or if at any time you feel this exercise is too challenging, feel free to skip to Chapter 2.

Quickstart is a simple AJAX form-validation application where the user is requested to type his or her name, and the server keeps verifying if it recognizes the typed name while the user is writing. Figure 1-8 shows the initial page, Quickstart.html, loaded by the user.

Figure 1-8 The Front Page of Your Quickstart Application

While the user is typing, the server is being called asynchronously, at regular intervals, to validate the current user input. The server is called automatically, approximately once per second, which explains why we don’t need a button (such as a Send button) to notify when we’re done typing. (This method may not be appropriate for real log-in mechanisms but it’s very good to demonstrate the basic AJAX functionality).

Depending on the entered name, the message from the server may differ; see an example in Figure 1-9.

Figure 1-9 User Receives a Prompt Reply From the Web Application

Check out this example online at http://www.cristiandarie.ro/asp-ajax/Quickstart.html . Maybe at first sight there’s nothing extraordinary going on there. We’ve kept this first example simple on purpose, to make things easier to understand. What’s special about this application is that the displayed message comes automatically from the server, without interrupting the user’s actions. (The messages are displayed as the user types a name.) The page doesn’t get reloaded to display the new data, even though a server call needs to be made to get that data. This wasn’t a simple task to accomplish using non-AJAX web development techniques. The application consists of the following three files:

  • Quickstart.html is the initial HTML file the user requests.

  • Quickstart.js is a file containing JavaScript code that is loaded on the client along with Quickstart.html. This file will handle making the asynchronous requests to the server, when server-side functionality is needed.

  • Quickstart.aspx is an ASP.NET page residing on the server that gets called by the JavaScript code in Quickstart.js file from the client.

Figure 1-10 shows the actions that happen when running this application:

Figure 1-10 Diagram Explaining the Inner Works of Your Quickstart Application

Steps 1 through 5 are a typical HTTP request. After making the request, the user needs to wait until the page gets loaded. With typical (non-AJAX) web applications, such a page reload happens every time the client needs to get new data from the server.

Steps 5 through 9 demonstrate an AJAX-type call—more specifically, a sequence of asynchronous HTTP requests. The server is accessed in the background using the XMLHttpRequest object. During this period the user can continue to use the page normally, as if it were a normal desktop application. No page refresh or reload is experienced in order to retrieve data from the server and update the web page with that data.

Now it’s time to implement this code on your machine. Before moving on, ensure you’ve prepared your working environment as shown earlier in this chapter.

Note

All the exercises in this book assume that you’ve installed software on your machine as shown earlier in this chapter. If you set up your environment differently you may need to implement various changes, such as using different folder names, and so on.

Time for Action—Quickstart AJAX

  1. 1. Open Visual Web Developer, and select File | Open Web Site.

  2. 2. Select the Local IIS location, and the Atlas application, as shown in Figure 1-11. Then click OK.

    Figure 1-11 Loading the Atlas Application in Visual Web Developer

    Note

    If you don’t have IIS, or intend to use Cassini for any reason, set the Location type to File System, and type C:\Atlas\ for the location.

  3. 3. Right-click the root node in Solution Explorer, and select Add New Item. Choose the HTML Page template, and type Quickstart.html for the name. Then click Add.

  4. 4. Modify the HTML code generated for you like this:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
      <head>
        <title>AJAX with ASP.NET: Quickstart</title>
        <script type="text/javascript" src="Quickstart.js"></script>
      </head>
      <body onload="process()">
        <div>
          Server wants to know your name:
          <input type="text" id="myName" />
        </div>
        <div id="divMessage" />
      </body>
    </html>
  5. 5. Right-click the root node in Solution Explorer, then click Add New Item, and add a file named Quickstart.js using the JScript File template. Then type the following code:

    // stores a reference to an XMLHttpRequest instance
    var xmlHttp = createXmlHttpRequestObject();
    
    // retrieves the XMLHttpRequest object
    function createXmlHttpRequestObject()
    {
      // will store the reference to the XMLHttpRequest object
      var xmlHttp;
      // this should work for all browsers except IE6 and older
      try
      {
        // try to create XMLHttpRequest object
        xmlHttp = new XMLHttpRequest();
      }
      catch(e)
      {
         // assume IE6 or older
         xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      // return the created object or display an error message
      if (!xmlHttp)
        alert("Error creating the XMLHttpRequest object.");
      else
        return xmlHttp;
     }
      // make asynchronous HTTP request using the XMLHttpRequest object
      function process()
      {
         // proceed only if the xmlHttp object isn't busy
         if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
         {
           // retrieve the name typed by the user on the form
           name = encodeURIComponent(document.getElementById("myName").
                                                                value);
           // execute the Quickstart.aspx page from the server
           xmlHttp.open("GET", "Quickstart.aspx?name=" + name, true);
           // define the method to handle server responses
           xmlHttp.onreadystatechange = handleServerResponse;
           // make the server request
           xmlHttp.send(null);
         }
         else
           // if the connection is busy, try again after one second
           setTimeout("process()", 1000);
      }
      // executed automatically when a message is received from server
      function handleServerResponse()
      {
        // move forward only if the transaction has completed
        if (xmlHttp.readyState == 4)
        {
          // status of 200 indicates success
          if (xmlHttp.status == 200)
          {
            // extract the XML retrieved from the server
            xmlResponse = xmlHttp.responseXML;
            // obtain the root element of the XML structure
            xmlDocumentElement = xmlResponse.documentElement;
            // get the text message from the first child of the document
            helloMessage = xmlDocumentElement.firstChild.data;
            // display the data received from the server
            document.getElementById("divMessage").innerHTML =
              "<i>" + helloMessage + "</i>";
            // restart sequence
            setTimeout("process()", 1000);
          }
          // a HTTP status different than 200 signals an error
          else
          {
            alert("There was a problem accessing the server: " +
                                              xmlHttp.statusText);
          }
       }
     }
  6. 6. Create a new file named Quickstart.aspx in your project using the Web Form template. Choose Visual C# for the language, and make sure both the Place code in separate file and Select master page checkboxes are unchecked. While using code-behind files is a good idea in most cases, this time we don’t want to complicate things unnecessarily.

  7. 7. Delete the template code generated by Visual Web Developer for you, and type the following code in Quickstart.aspx:

    <script runat="server" language="C#">
      protected void Page_Load()
      {
         // declare the names that are recognized by the server
         string[] names = new string[] { "CRISTIAN", "BOGDAN", "YODA" };
    
        // retrieve the current name sent by the client
         string currentUser = Request.QueryString["name"] + "";
    
        // set the response content type
        Response.ContentType = "text/xml";
    
        // output the XML header
        Response.Write("<?xml version=\"1.0\" encoding=\"UTF-8\"
                                           standalone=\"yes\"?>");
        Response.Write("<response>");
    
         // if the name is empty...
         if (currentUser.Length == 0)
         {
           Response.Write("Stranger, please tell me your name!");
         }
         // if the typed name is in the names array
         else if (Array.IndexOf(names, currentUser.ToUpper().Trim()) >= 0)
         {
           Response.Write("Hello, master " + currentUser + "!");
         }
         // if the name is neither empty or recognized
         else
         {
           Response.Write(currentUser + ", I don't know you!");
         }
    
         // output the XML document
        Response.Write("</response>");
    
        // flush the response stream
        Response.Flush();
      }
    </script>
  8. 8. Double-click Quickstart.html in Solution Explorer, and press F5 to execute the project in debug mode (You can also execute the project without debugging, by hitting CTRL+F5). Visual Web Developer will offer to enable debugging by creating a Web.config file with the appropriate settings for you, as shown in Figure 1-12. Click OK.

    Figure 1-12 Visual Web Developer Offering to Enable Debugging

  9. 9. A browser window will load http://localhost/Atlas/Quickstart.html, which should look like shown earlier in Figures 1-9 and 1-10.

Note

Should you encounter any problems running the application, check whether you correctly followed the installation and configuration procedures. Most errors happen because of small problems such as typos. In Chapter 2 you’ll learn how to implement error handling in your JavaScript and ASP.NET code.

What Just Happened?

Here comes the fun part—understanding what happens in that code. (Remember that we’ll discuss the technical details in the following chapters.)

Let’s start with the file the user first interacts with, Quickstart.html. This file references the mysterious JavaScript file called Quickstart.js, and builds a very simple web interface for the client.

In the following code snippet from Quickstart.html, notice the elements highlighted in bold:

<body onload="process()">
  <div>
    Server wants to know your name:
    <input type="text" id="myName" />
  </div>
  <div id="divMessage" />
</body>

When the page loads, a function from Quickstart.js called process() is executed. This somehow causes the <div> element to be populated with a message from the server. Before seeing what happens inside the process() function, let’s see what happens at the server side.

The server is represented by a page called Quickstart.aspx. This page receives the name typed by the visitor, and it replies with an XML message. You may want to have another look at Figure 1-10, which describes the process. This XML message Quickstart.aspx sends back to the client consists of a <response> element that packages the response message:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
  ... message the server wants to transmit to the client ...
</response>

If the user name received from the client is empty, the message will be, “Stranger, please tell me your name!”. If the name is Cristian, Bogdan, or Yoda, the server responds with “Hello, master <user name>!”. If the name is anything else, the message will be “<user name>, I don’t know you!”. So if Mickey Mouse types his name, the server will send back the following XML structure:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
  Mickey Mouse, I don't know you!
</response>

If you want to test this actually happens, it’s quite simple. The advantage of sending parameters from the client via GET is that it’s very simple to emulate such a request using your web browser, since GET simply means that you append the parameters as name/value pairs in the URL query string. So to simulate the server request done by the client when the user types Yoda, simply load http://localhost/Atlas/Quickstart.aspx?name=Yoda into your web browser. You should get the XML response shown in Figure 1-13.

Figure 1-13 The XML Data Generated by Quickstart.aspx

The Quickstart.aspx page contains a simple script that contains the Page_Load() method, which executes by default when the script is accessed. Page_Load() starts by declaring the array of strings that will be the names “known” by the server:

  protected void Page_Load()
  {
    // declare the names that are recognized by the server
    string[] names = new string[] { "CRISTIAN", "BOGDAN", "YODA" };

Next, the Page_Load() method contains the entire server logic behind this example. It retrieves the name sent by the client:

// retrieve the current name sent by the client
string currentUser = Request.QueryString["name"] + "";

The value of the name query string parameter is read using Request. QueryString["name"]. This returns null when name doesn’t exist in the query string, and we use a little trick—appending an empty string to it—to avoid errors when this happens. (In production code, when performance is a factor, you may prefer to use different error-avoiding techniques.)

Next, we set the content type of the response to text/xml, and we start building the XML response by opening the <response> element:

    // set the response content type
    Response.ContentType = "text/xml";
    // output the XML header
    Response.Write("<?xml version=\"1.0\" encoding=
                                \"UTF-8\" standalone=\"yes\"?>");
    Response.Write("<response>");

The Response.ContentType property corresponds to the Content-Type HTTP header. We use it to set the content type to text/xml, which is appropriate when the page sends back an XML structure. Response.Write() is used to send content to the output. The first bits we output are the XML document definition, and the <response> document element.

We continue by writing the message itself to the output. If the name is an empty string, the message will be “Stranger, please tell me your name!”

// if the name is empty...
if (currentUser.Length == 0)
{
  Response.Write("Stranger, please tell me your name!");
}

If the name is one of those in the names array, the message will be a bit more friendly:

// if the typed name is in the names array
else if (Array.IndexOf(names, currentUser.ToUpper().Trim()) >= 0)
{
  Response.Write("Hello, master " + currentUser + "!");
}

Here’s the code that outputs the text in case the name isn’t recognized:

// if the name is neither empty or recognized
else
{
  Response.Write(currentUser + ", I don't know you!");
}

Finally, we close the <response> element, and flush the response stream:

    // output the XML document
    Response.Write("</response>");


    // flush the response stream
    Response.Flush();
}

This XML message outputted by the server (Quickstart.aspx) is read at the client by the handleServerResponse() function in Quickstart.js. More specifically, the following lines of code extract the Hello, master Yoda! message, assuming the reader has typed Yoda in the text box:

// extract the XML retrieved from the server
xmlResponse = xmlHttp.responseXML;
// obtain the document element (the root element) of
the XML structure
xmlDocumentElement = xmlResponse.documentElement;
// get the text message, which is in the first child of
the document element
helloMessage = xmlDocumentElement.firstChild.data;

Here, xmlHttp is the XMLHttpRequest object used to call the server page Quickstart.aspx from the client. Its responseXML property extracts the retrieved XML document. XML structures are hierarchical by nature, and the root element of an XML document is called the document element. In this case, the document element is the <response> element, which contains a single child, which is the text message we’re interested in. Once the text message is retrieved, it’s displayed on the client’s page by using the DOM to access the <divMessage> element in Quickstart.html:

// update the client display using the data received
from the server
document.getElementById("divMessage").innerHTML = helloMessage;

document is a default object in JavaScript that allows you to manipulate the elements in the HTML code of your page.

The rest of the code in Quickstart.js deals with making the request to the server to obtain the XML message. The createXmlHttpRequestObject() function creates and returns an instance of the XMLHttpRequest object. This function is longer than it could be because we need to make it cross-browser compatible—we’ll discuss the details in Chapter 2. For now, it’s important to know what it does. The XMLHttpRequest instance, called xmlHttp, is used in process() to make the asynchronous server request:

// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
  // proceed only if the xmlHttp object isn't busy
  if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
  {
    // retrieve the name typed by the user on the form
    name = encodeURIComponent(document.getElementById("myName").
                                                          value);
    // execute the Quickstart.aspx page from the server
    xmlHttp.open("GET", "Quickstart.aspx?name=" + name, true);
    // define the method to handle server responses
    xmlHttp.onreadystatechange = handleServerResponse;
    // make the server request
    xmlHttp.send(null);
  }
  else
    // if the connection is busy, try again after one second
    setTimeout("process()", 1000);
}

What you see here is, actually, the heart of AJAX—the code that makes the asynchronous call to the server.

You may wonder why it is it so important to call the server asynchronously. Asynchronous requests, by their nature, don’t freeze processing (and with it the user experience) from when the call is made until the response is received. Asynchronous processing is implemented by event-driven architectures, a good example being the way graphical user interface code is built. Without events, you’d probably need to check continuously if the user has clicked a button or resized a window. Using events, the button notifies the application automatically when it has been clicked, and you can take the necessary actions in the event handler function. With AJAX, this theory applies when making a server request—you are automatically notified when the response comes back.

If you’re curious to see how the application would work using a synchronous request, change the third parameter of xmlHttp.open() to false, and then call handleServerResponse() as shown below. If you try this, the input box where you’re supposed to write your name will freeze when the server is contacted (although the delay may not noticeable when testing this on the local machine).

// function calls the server using the XMLHttpRequest object
function process()
{
  // retrieve the name typed by the user on the form
  name = encodeURIComponent(document.getElementById("myName").value);
  // execute the Quickstart.aspx page from the server
xmlHttp.open("GET", "Quickstart.aspx?name=" + name, false);
// synchronous server request (freezes processing until completed)
xmlHttp.send(null);
// read the response
handleServerResponse();
}

The process() function is supposed to initiate a new server request using the XMLHttpRequest object. However, this is only possible if the XMLHttpRequest object isn’t busy making another request. In our case, this can happen if it takes more than one second for the server to reply, which could happen if the Internet connection is very slow. So process() starts by verifying that it is clear to initiate a new request. Chapter 2 will show more details about the various possible states of the request, but for now, it’s enough to know that a state of 0 or 4 means the connection is available to make a new request. (It’s not possible to perform more that one request through a single XMLHttpRequest object at any given time.)

// make asynchronous HTTP request using the XMLHttpRequest object
function process()
{
  // proceed only if the xmlHttp object isn't busy
  if (xmlHttp.readyState == 4 || xmlHttp.readyState == 0)
  {

So, if the connection is busy, we use setTimeout() to retry after one second (the function’s second argument specifies the number of milliseconds to wait before executing the piece of code specified by the first argument:

// if the connection is busy, try again after one second
setTimeout("process()", 1000);

If the line is clear, you can safely make a new request. The lines of code that prepare the server request but don’t commit it:

// execute the Quickstart.aspx page from the server
xmlHttp.open("GET", "Quickstart.aspx?name=" + name, true);

The first parameter specifies the method used to send the user name to the server, and you can choose between GET and POST (you will learn more about them in Chapter 2). The second parameter is the server page you want to access; when the first parameter is GET, you send the parameters as name/value pairs in the query string. The third parameter is true if you want the call to be made asynchronously. When making asynchronous calls, you don’t wait for a response. Instead, you define another function to be called automatically when the state of the request changes:

// define the method to handle server responses
xmlHttp.onreadystatechange = handleServerResponse;

Once you’ve set this option, you can rest calm—the handleServerResponse() function will be executed by the system when anything happens to your request. After everything is set up, you initiate the request by calling the XMLHttpRequest object’s send method:

  // make the server request
  xmlHttp.send(null);
}

Let’s now look at the handleServerResponse() function:

// executed automatically when a message is received from the server
function handleServerResponse()
{
  // move forward only if the transaction has completed
  if (xmlHttp.readyState == 4)
  {
// status of 200 indicates the transaction completed successfully
if (xmlHttp.status == 200)
{

The handleServerResponse() function is called multiple times, whenever the status of the request changes. The data received from the server can be read only if xmlHttp.readyState is 4 (which happens when the response was fully received from the server), and the HTTP status code is 200, signaling that there were no problems during the HTTP request. We’ll learn more about these concepts in chapter 2. For now, suffice to say that when they are met, you can read the server response, which you can use to display an appropriate message to the user.

After the response is received and used, the process is restarted using the setTimeout() function, which will cause the process() function to be executed after one second (although that it’s not necessary, even with AJAX, to have repetitive tasks in your client-side code):

// restart sequence
setTimeout("process()", 1000);

Finally, let’s reiterate what happens after the user loads the page (you can refer to Figure 1-6 for a visual representation):

  1. 1. The user loads Quickstart.html (this corresponds to steps 1–4 in Figure 1-10).

  2. 2. The user starts (or continues) typing his or her name (this corresponds to step 5 in Figure 1-10.

  3. 3. When the process() method in Quickstart.js is executed, it calls a server script named Quickstart.aspx asynchronously. The text entered by the user is passed on the call as a query string parameter (via GET). The handeServerResponse() function is designed to handle request state changes.

  4. 4. Quickstart.aspx executes on the server. It composes an XML document that encapsulates the message the server wants to transmit to the client.

  5. 5. The handleServerResponse() method on the client is executed multiple times as the state of the request changes. It’s called for the last time when the response has been successfully received. The XML is read; the message is extracted and displayed on the page.

  6. 6. The user display is updated with the new message from the server, but the user can continue typing without any interruptions. After a delay of one second, the process is restarted from step 2.