A lot of focus on ASP.NET Ajax ends up being targeted at the cool out-of-the-box server-side controls that it offers - the UpdatePanel and all the extenders in the AjaxControlToolkit etc. Underneath the hood though, theres an impressive client side JavaScript framework that makes all of these things actually work. As part of this framework, the JavaScript language itself has been 'extended' to support familiar .NET concepts like namespaces and classes, among other things. In this post, I'll show a pattern for creating a static (or maybe we should call it a singleton?) class using these extentions.
Although I'll explain each step along the way, I will as usual make the assumption that this is not the first time my readers come across most of the concepts discussed here - a fair grasp of JavaScript and the ASP.NET Ajax client-side framework in general should probably be considered a prerequisite for the remainder of this post. That said, lets get on with it! :)
The first thing we need to do, is register the namespace that we wish to put our class in:
Type.registerNamespace("Iridescence");
The Type class is defined in the Global Namespace of the ASP.NET Ajax extentions for JavaScript, and "provides a typing and type-reflection system for JavaScript object-oriented programming functionality".
Next, we define the class:
Iridescence._MyStaticClass = function Iridescence$_MyStaticClass()
{}
The underscore prefix used here is merely a convention - javascript lacks the notion of access modifiers, and the _ simply means that this class should be considered private.
Now begins the 'magic'. We declare a private getInstance() method that will serve as a singelton instance accessor of sorts:
function Iridescence$_MyStaticClass$_getInstance()
{
if (!this._instance)
{
this._instance = new Iridescence._MyStaticClass();
}
return this._instance;
}
Here, 'this' will be referring to the prototype of our class, which we'll be defining next. First however, we should now declare the static 'proxies' for any methods we want our class to expose:
function Iridescence$_MyStaticClass$sayHello()
{
return this._getInstance().sayHello();
}
function Iridescence$_MyStaticClass$setName(value)
{
return this._getInstance().setName(value);
}
That done, we move on to the prototype definition:
Iridescence._MyStaticClass.prototype =
{
_instance: null,
_name: null,
sayHello : function()
{
return confirm('hello, ' + this._name + '?');
},
setName : function(name)
{
this._name = name;
}
}
If you're unfamiliar with the concept of a prototype (should not be confused with the JavaScript framework Prototype), there are some good definitions here and here. Basically, JavaScript is what we call a prototype-based programming scripting language, which means that objects are created by cloning its prototype instead of new'ing them from 'nothing', like we do in C#. Above, we've defined the prototype for our class, which contains a few fields and methods that the static 'proxies' we defined earlier uses.
We're almost done now, we just need to register our class:
Iridescence._MyStaticClass.registerClass('Iridescence._MyStaticClass');
And finally, set up the public instance of it that we will use to access the static functions:
Iridescence.MyStaticClass = new Iridescence._MyStaticClass();
And thats it! Now, if we wanted to use this class, it would look something like this:
<asp:Button
runat="server"
OnClientClick="var name = prompt('Name?',''); Iridescence.MyStaticClass.setName(name); return false"
Text="Set name" />
<asp:Button
runat="server"
OnClientClick="return Iridescence.MyStaticClass.sayHello();"
Text="Say hello" />
Take a minute to get your head around where those method calls are really going... 'Iridescence' is really just an object that represents the 'namespace' Iridescence, and was created by the first call we made to Type.registerNamespace(). 'Iridescence.MyStaticClass' then, is really just a field on that object, which we've assigned an instance of the class _MyStaticClass. This object instance has a set of 'static' methods, which when called gets a reference to another, (internal, if you like) singleton instance of _MyStaticClass (held by the _instance field within the prototype), on which all these methods call operate.
Got it? Phew... clever stuff!
As a final note, I feel I should mention that I didn't come up with this 'pattern', if one could call it that - it is used several places within the ASP.NET Ajax framework, for instance in the implementation of DragDropManager (which, I believe, is used by www.live.com).
Download the source code here (Visual Studio 2008 Beta 2 solution file).