Wednesday, March 4, 2009

Making a call to the server from JavaScript using ASP.NET Client Callback (Low Level AJAX)

The concept behind AJAX is really nothing new. The basic idea is that from JavaScript we want to call a server function and have the result returned to us. The result is returned by calling a JavaScript callback function such that the result from the server is sent via a parameter to this JavaScript callback function. Typically we want to display the result dynamically by updating the HTML page we are on without a postback. To do this, the JavaScript callback function typically will change the page using JavaScript.

In ASP.NET, you can use update panels, and other high level methods of doing this, but this is essentially what is going on behind the scene. Sometimes, you may want or need to do the same thing but at a very low level.  

To add this type of functionality to your ASP.NET page, here is a list of simple ways (not the only ways) you can add this low level AJAX to your page.

Make the class for your page implement System.Web.UI.ICallbackEventHandler interface.

This interface requires that you define the following two methods.

   
string result;
    // Define method that processes the callbacks on server.
    // NOTE: This is required by the ICallbackEventHandler interface
    public void RaiseCallbackEvent(String eventArgument)
    {
        result = eventArgument + "!!!!";
    }


    // Define method that returns callback result.
    // NOTE: This is required by the ICallbackEventHandler interface
    public string GetCallbackResult()
    {
        return result;
    }

Since AJAX is asynchronous that means that after the RaiseCallbackEvent method is called that processing can take a long time, and will not block further execution. This means that when the processing is done we need to have some way of knowing that, and then doing something with that result. In our example, I am simply adding "!!!!" to the value, but this could very complex long running code. That is what the GetCallbackResult method is for. It gets called only when the RaiseCallbackEvent method has completed. You don't call it, the framework does this for you.

You may notice that we use a result variable to communicate between the two methods. I think this is a bit strange, and would have expected the result to be passed as an parameter to GetCallbackResult, but it wasn't. :) I expect this approach is nearly as simple, and arguably more flexible.

Now that we have defined what will be happen when the AJAX request is received by the server, we need to define what will happen when data is passed back to the JavaScript page.

For this, let's assume we want to display the results in a span tag on our page. For this let's assume we have something like the following defined somewhere on our page.

<span id="MyMessage">Original Content Here</span>

We need to define one methods to do something with the result once it is sent back to the browser. The result shows itself as a parameter to the JavaScript function. It would be best to define one that will get called if there is an error with the AJAX call.

The JavaScript Function would be something like this.

function DoSomethingCallback(text)
{
  var myMessage = document.getElementById("MyMessage");
  MyMessage.innerHTML = text;
}

function DoSomethingErrorHandler(text)
{
  var myMessage = document.getElementById("MyMessage");
  MyMessage.innerHTML = 'An error has occurred.';
}

 
Now all we need to do is wire up the callback. This is done by asking ASP.NET to create the callback for us. Here is the C# code that must fire on EVERY page load. So, put it in the page load event or some other event that occurs EVERY time the page loads. If you don't do this on every page load, you will get postbacks instead of AJAX calls.

In the method below the first parameter is the page that implements the ICallbackEventHandler. The second parameter is in this case the name of parameter, but it could also be a global JavaScript variable, or a constant. If it is a string constant, you will need to use the embedded single quotes. The third parameter is the name of the JavaScript function we defined above that handles the returned data. The fourth parameter is the name of the JavaScript function we defined above that handles the error. The last parameter is to designate we are doing this asynchronously.

// Get the JavaScript Callback
String jscriptCallback = Page.ClientScript.GetCallbackEventReference(this, "val", "DoSomethingCallback", "",
"DoSomethingCallbackErrorHandler", false);


In theory we are done. You can now use the value of the jscriptCallback C# variable however you want. Now, however you want to get that JavaScript code to the browser is up to you. In this example, I am taking some shortcuts to make understanding the example a bit easier. However, in real life just like other JavaScript stuff in ASP.NET, you would want to use RegisterClientScriptBlock to define the JavaScript function, instead of using a property as I have done below.

For our example, I am adding a property like the following.

public string AjaxJavaScript
{
   get
   {
      // Get the JavaScript Callback
      String jscriptCallback = 
         Page.ClientScript.GetCallbackEventReference(this, "val", 
      "DoSomethingCallback", "",
      "DoSomethingCallbackErrorHandler", false);
      return jscriptCallback;
   }
}

Now I can use this anywhere in my .aspx page to get access to the code. In most cases you will need to wrap this code in a JavaScript function since the code generated by ASP.NET has both single and double quotes. This makes it very difficult to use directly in an event handler that is defined in quotes or double-quotes. Here is the example JavaScript function.

function GetMessageFromServer(val)
{
  <%= AjaxScript %>
}

If you view the source on the page, you would see that the code looks something like the following.

function GetMessageFromServer(val)
{
WebForm_DoCallback('__Page',val,DoSomethingCallback,"",DoSomethingCallbackErrorHandler,false);
}


Notice that the second parameter here is val which must match the parameter of the surrounding function.

Now for the exciting part. Now we can actually call the function from anywhere we can use JavaScript. In the below example, I am calling the JavaScript from two HTML buttons.

<input type="button" value="12345" onclick="GetMessageFromServer(12345)"/>
<input type="button" value="111" onclick="GetMessageFromServer(111)"/>



Here is the complete source for easy copy and paste:


<%@ Page Language="C#" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<script runat="server">
   
    string result;
    // Define method that processes the callbacks on server.
    // NOTE: This is required by the ICallbackEventHandler interface
    public void RaiseCallbackEvent(String eventArgument)
    {
        result = eventArgument + "!!!!";
    }


    // Define method that returns callback result.
    // NOTE: This is required by the ICallbackEventHandler interface
    public string GetCallbackResult()
    {
        return result;
    }



public string AjaxJavaScript
{
    get
    {
       // Get the JavaScript Callback
       String jscriptCallback = Page.ClientScript.GetCallbackEventReference(this, "val",
       "DoSomethingCallback", "",
       "DoSomethingCallbackErrorHandler", false);
    return jscriptCallback;
    }
}
   
</script>


 


<html>
<head>
    <title>Low Level AJAX Example</title>
   
    <script type="text/javascript">
function DoSomethingCallback(text)
{
  var myMessage = document.getElementById("MyMessage");
  myMessage.innerHTML = text;
}


function DoSomethingCallbackErrorHandler(text)
{
     var myMessage = document.getElementById("MyMessage");
  myMessage.innerHTML = 'An error has occurred.';
}


function GetMessageFromServer(val)
{
     //Example: WebForm_DoCallback('__Page',val,DoSomethingCallback,"",DoSomethingCallbackErrorHandler,false);
     <%= AjaxJavaScript %>
}


</script>
</head>
<body>
    <form id="Form1" runat="server">
  <span id="MyMessage">Original Content Here</span>
  <input type="button" value="12345" onclick="GetMessageFromServer(12345)"/>
  <input type="button" value="111" onclick="GetMessageFromServer(111)"/>
    </form>
</body>
</html>


No comments: