Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

AJAX Was Here - Part 2: ASP.NET Integration

0.00/5 (No votes)
2 May 2005 3  
Asynchronous JavaScript and XML with ASP.NET.

Introduction

This is the second article in a three article series focusing on AJAX or Asynchronous JavaScript and XML. If you haven�t read Part 1, I encourage you to read Part 1 of this article series. In Part 1, we had covered the client side portion of AJAX, specifically the JavaScript object used to initiate asynchronous web requests. The goal of this article is to provide something easy for the server side (ASP.NET) developer to integrate into their code (preferably without a lot of fancy or proprietary workarounds).

The Good Stuff

We will be going over the example code (see Download source files above) shown in listings 1 and 2, Article.aspx and Article.aspx.cs respectively. The code samples are shown in C# but the technique can be used with any .NET compliant language.

Article.aspx

Article.aspx is an example of how you might apply AJAX to a real web application. More and more web sites are requiring users to register before they can access most of the sites� content. This usually involves selecting a username and password as well as entering some other information like an email address or zip code. Rarely does a site allow two users to have the same username. So let�s say I choose �JohnDoe� as my username, I click the button to sign up, wait a while, only to have the web page reload saying that the username I have requested is not available. This process could go on for a while until I choose a unique username. AJAX allows us to validate the username as the user is typing it. No posting back to the server and no downloading a complete list of unavailable usernames. Article.aspx shows this �instant validation� in action.

The JavaScript

To validate the username being entered, we need to send the current username to the server, wait for the request to complete, then take action based on the result. I have simplified this process by creating the CallBackObject (see AJAX Was Here � Part 1 for the details).

var Cbo = new CallBackObject();
Cbo.OnComplete = Cbo_Complete;
Cbo.OnError    = Cbo_Error;

Here we are creating a new CallBackObject and telling the object, �When my web request is completed, I want you to run the function Cbo_Complete�, and �If anything bad happens during the request, I want you to run the function Cbo_Error�. Since the web request is asynchronous, we don�t know when exactly the server will complete our request, so we setup the OnComplete event so we don�t have to sit and twiddle our thumbs.

function Cbo_Complete(responseText, responseXML)
{
  var msg = document.getElementById('lblMessage');
  if( responseText == 'True' )
  {
    msg.innerHTML = 'CallBack - Username Available!';
    msg.style.color = 'green';
  }
  else
  {
    msg.innerHTML = 'CallBack - Username Unavailable!';
    msg.style.color = 'red';
  }
}

When our web request does finish, we want to let the user know. Our web request will return True if the username entered is available, or False if the username entered is not available. If the username is available, we show a positive message with green text, otherwise, we show a negative message in red text.

function Cbo_Error(status, statusText, responseText)
{
  alert(responseText);
}

If an error occurs during the web request, we show the error using a standard alert box.

function CheckUsername(Username)
{
  var msg = document.getElementById('lblMessage');
  if( Username.length > 0 )
  {
    Cbo.DoCallBack('txtUsername', '');
  }
  else
  {
    Cbo.AbortCallBack();
    msg.innerHTML = '';
  }
}

The function CheckUsername starts the asynchronous request (or Call Back) to the server. First, we make sure that the username in question is not blank, and then we call Cbo.DoCallBack, passing the ID of the username input box (more on this later). If the username is blank, we cancel any Call Back currently in process, and clear any messages.

<asp:TextBox id=txtUsername 
  onkeyup=CheckUsername(this.value); 
  OnTextChanged="txtUsername_TextChanged" 
  Runat="server"></asp:TextBox>

Finally, within the HTML, we set the onkeyup attribute of our username textbox to execute CheckUsername and pass the current value of the textbox. We use onkeyup so that we can provide instant feedback to the user as they are entering their desired username.

That is all the client side code required to make our AJAX example work.

The ASP.NET

The beauty of the CallBackObject is that it essentially allows JavaScript code to fire server side events. Let�s look at the HTML snippet again.

<asp:TextBox id=txtUsername 
  onkeyup=CheckUsername(this.value); 
  OnTextChanged="txtUsername_TextChanged" 
  Runat="server"></asp:TextBox>

Notice how we set OnTextChanged (which is a server side event) to txtUsername_TextChanged. As we will see in a moment (see Listing 2), txtUsername_TextChanged is an ASP.NET event written in C# that determines if the value of txtUsername is an available username. This event is raised from the client using JavaScript by this line:

Cbo.DoCallBack('txtUsername', '');

Isn�t that cool? You are raising server side events with client side code without reloading the entire page. Let�s look a little deeper at txtUsername_TextChanged.

protected void txtUsername_TextChanged(object sender, System.EventArgs e)
{
  if( !CallBackHelper.IsCallBack )
    return;

  string uName = txtUsername.Text;

  try
  {
    CallBackHelper.Write( IsUsernameAvailable(uName).ToString() );
  }
  catch( Exception ex )
  {
    CallBackHelper.HandleError( ex );
  }
}

First off, we check to see if the current request is a Call Back, using the CallBackHelper (included in the source files). This is exactly the same as using Page.IsPostBack, you want to do different things depending on the context of the request. Next, we get the value of txtUsername and pass it to our IsUsernameAvailable function. This function returns a boolean indicating whether or not the username is available. Finally, we write that value back to the client using CallBackHelper.Write. Notice how we wrap the processing in a try/catch block and if anything bad happens, we use CallBackHelper.HandleError. This makes sure that the client is notified of errors.

private bool IsUsernameAvailable( string Username )
{
  bool isAvailable = true;

  switch( Username.ToLower() )
  {
    case "bill":
    case "william":
    case "christopher":
    case "pierce":
    case "zonebit":
      isAvailable = false;
      break;
  }

  return isAvailable;
}

IsUsernameAvailable is a simple test function, normally you would lookup the requested username in a database to see if it was valid.

I have also but an asp:button on the page to show the difference between our call back implementation and the standard ASP.NET implementation. If you click the �Check Username Availability� button, you post back the entire page to validate the username, which may take a while longer, plus the user cannot continue filling out the form until the server returns the results.

Test It out

Cruise on over to this site to see AJAX in action. Type a letter, and you should get a visual indicator of the availability of the username. Continue typing and you should be updated as you go (try bill or pierce for an unavailable username). You can also click the button to perform the same action using a standard post back.

But what does it all mean?

We could have returned any string to the client, including XML. In this example we only need a simple true/false to indicate if the username is available. Another example might be sending a zip code from the client and returning the city and state from the server. The sky is the limit now that you can interact with server side code from the client.

Conclusion

ASP.NET takes care of the event plumbing for us. As long as you use ASP.NET controls and implement standard events, SelectedIndexChanged, TextChanged, Click, you can easily use AJAX in your ASP.NET applications with the CallBackObject and CallBackHelper.

And then?

If you�re not already tired of reading my rants, check out Part 3 of the article series when we create an Auto Complete Textbox. Auto Complete Textbox is an ASP.NET control that completes your text as you type. Awesome!

Listing 1 - Article.aspx

<%@ Page language="c#" Codebehind="Article.aspx.cs" 
  AutoEventWireup="false" Inherits="AJAX.Article" %>
  <HEAD>
    <title>AJAX was Here</title>
    <script type="text/javascript" src="CallBackObject.js"></script>
  </HEAD>
    <form id="frmAjax" method="post" runat="server">
      <script type="text/javascript">
        var Cbo = new CallBackObject();
        Cbo.OnComplete = Cbo_Complete;
        Cbo.OnError    = Cbo_Error;

        function CheckUsername(Username)
        {
          var msg = document.getElementById('lblMessage');
          if( Username.length > 0 )
          {
            Cbo.DoCallBack('txtUsername', '');
          }
          else
          {
            Cbo.AbortCallBack();
            msg.innerHTML = '';
          }
        }

        function Cbo_Complete(responseText, responseXML)
        {
          var msg = document.getElementById('lblMessage');
          if( responseText == 'True' )
          {
            msg.innerHTML = 'CallBack - Username Available!';
            msg.style.color = 'green';
          }
          else
          {
            msg.innerHTML = 'CallBack - Username Unavailable!';
            msg.style.color = 'red';
          }
        }

        function Cbo_Error(status, statusText, responseText)
        {
          alert(responseText);
        }
      </script>
      <table width="100%">
        <tr>
          <td>Username:</td>
          <td>
            <asp:TextBox Runat="server" ID="txtUsername" 
              onkeyup="CheckUsername(this.value);" 
              OnTextChanged="txtUsername_TextChanged" />
          </td>
          <td align="left" width="100%">
          <asp:Label Runat="server" ID="lblMessage"/>
          </td>
        </tr>
        <tr>
          <td colspan="3" align="left">
            <asp:Button Runat="server" ID="btnCheckUsername" 
              OnClick="btnCheckUsername_Click" 
              Text="Check Username Availability" />
          </td>
        </tr>
      </table>
    </form>

Listing 2 - Article.aspx.cs

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using WCPierce.Web;

namespace AJAX
{
  public class Article : System.Web.UI.Page
  {
    protected System.Web.UI.WebControls.TextBox txtUsername;
    protected System.Web.UI.WebControls.Label lblMessage;
    protected System.Web.UI.WebControls.Button btnCheckUsername;
  
    private void Page_Load(object sender, System.EventArgs e) { }

    #region Web Form Designer generated code

    protected void txtUsername_TextChanged(object sender, System.EventArgs e)
    {
      if( !CallBackHelper.IsCallBack )
        return;

      string uName = txtUsername.Text;

      try
      {
        CallBackHelper.Write( IsUsernameAvailable(uName).ToString() );
      }
      catch( Exception ex )
      {
        CallBackHelper.HandleError( ex );
      }
    }

    protected void btnCheckUsername_Click(object sender, System.EventArgs e)
    {
      string uName = txtUsername.Text;
      if( IsUsernameAvailable( uName ) )
      {
        lblMessage.Text = "Server - Username Available!";
        lblMessage.ForeColor = Color.Green;
        lblMessage.Visible = true;
      }
      else
      {
        lblMessage.Text = "Server - Username Unavailable!";
        lblMessage.ForeColor = Color.Red;
        lblMessage.Visible = true;
      }

      //Simulate 5 second delay

      System.Threading.Thread.Sleep(5000);
    }

    private bool IsUsernameAvailable( string Username )
    {
      bool isAvailable = true;

      switch( Username.ToLower() )
      {
        case "bill":
        case "william":
        case "christopher":
        case "pierce":
        case "zonebit":
          isAvailable = false;
          break;
      }

      return isAvailable;
    }
  }
}

History

  • 2005-04-21 � Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here