17 October 2009

Using the Microsoft Ajax library to keep the javascript "this" context intact

This is not strictly .NET but more of a Javascript trick, but nevertheless, one that you might need. It is a simple trick, and actually requires much more text to decribe the problem than the actual solution ;-) Javascript supports the notion of object oriented programming, to an extent. There's plenty to find about that on the web and I don't care to repeat that. One of the more annoying things of Javascript is that it does support callbacks, but is quite careless about object context. This happens for instance when you use callbacks to process data coming back from calling WCF services. Suppose, for instance, I take my project in which I explain how to get data from WCF services in Javascript and rewrite the Javascript code as an object:
function CallBackProcessor()
{
  this.CallSingle = function()
  {
      dotnetbyexample.JSONDemoService.IJSONService.GetSingleObject(
      document.getElementById("tbId").value,
      document.getElementById("tbName").value,
      this.CallBackSingle);
  }
  this.CallBackSingle = function(WebServiceResult)
  {
      resultDiv = document.getElementById("result1");
      resultDiv.innerHTML = this.CreateMessage(WebServiceResult);
  }
  this.CreateMessage = function(WebServiceResult)
  {
    return( "You entered: id = " +
      WebServiceResult.Id + " name = " +
      WebServiceResult.Name);
  }            
}
var processor = new CallBackProcessor();
and then change the code of the button calling the service to call the processor object
<asp:Button ID="btnSelect" runat="server" Text="SingleSelect" 
   UseSubmitBehavior="False"
   OnClientClick="processor.CallSingle(); return false;" />
I will get the notorious "Object doesn't support this property or method" error. Why? Because I try to call the method "CreateMessage" from the "CallBackSingle": the context of "this" is no longer that of "processor", but of whatever is calling the callback. You can find this out by placing a breakpoint before the call to CreateMessage and then view the properties of "this". The logic behind this behaviour defies my imagination, but the solution is actually pretty simple: use Function.createDelegate to set up the callback in stead of calling it directly
this.CallSingle = function()
{
    dotnetbyexample.JSONDemoService.IJSONService.GetSingleObject(
    document.getElementById("tbId").value,
    document.getElementById("tbName").value,
    Function.createDelegate(this, this.CallBackSingle));
}
And that's all there is to it. The "this" context stays intact, thanks to the Microsoft Ajax library, that you already included automatically by placing a ScriptManager on your page.