Gotchas and learnings from refactoring to use ASP.NET AJAX

I promise that one of these days soon I'm going to talk a little about where POP Forums v8 is headed, and what you'll be able to do with it. I don't imagine there's a huge demand for the app, but regardless, it will be available.

In any case, I spent some time the last few evenings refactoring most of the Javascript stuff in the app. Some of it is bona fide AJAX, but other pieces are just using the framework to make Javascript seem more object-oriented and reusable. Along the way, I've learned some things that hopefully will at least make their way into search results so others don't have to endure similar pain.

Delegates are your weird friends

Describing delegates in C# is hard enough sometimes. In Javascript it's even more weird. There are several areas where you absolutely need to know how they're used. One of the more useful things already in Javascript is the setInterval method, which allows you to fire off some event at a regular interval. It takes the method you want to fire and the interval in milliseconds in which you want to fire it as parameters. With straight blocks of script, functions and such, it's easy enough to just pass in the method name. When used inside of a method in a prototype, it has to be wrapped in the Function.createDelegate() function provided by the framework. Take this class, for example...

PopForums.UI.ExpandContract = function(boxID, selectorText) {
    this._boxID = boxID;
    this._selectorText = selectorText;
    this._inertiabase = 5;
    this._interval = 10;
    this._inertiabaseoriginal = this._inertiabase;
    this._slideinterval = 0;
    this._newHeight = 0;
    this._box = $get(boxID);
}

PopForums.UI.ExpandContract.prototype = {
    executeExpandContract : function() {
        this._targetHeight = this._getHeight().replace("px", "");
        if (this._box.style.display == "none" || this._box.style.display == "") {
            // expand
            this._slideinterval = setInterval(Function.createDelegate(this, this.expand), this._interval);
        }
        else {
            // shrink
            this._newHeight = this._targetHeight;
            this._slideinterval = setInterval(Function.createDelegate(this, this.shrink), this._interval);
        }
    },
   
    shrink : function() {
        if (this._newHeight > 1) {
            this._newHeight = this._newHeight - this._inertiabase;
            this._inertiabase += 1;
            this._box.style.height = (this._newHeight > 1) ? this._newHeight + "px" : "1px";
        }
        else {
            clearInterval(this._slideinterval);
            this._box.style.display = "none";
        }
    },
    expand : function() {...

This class opens and closes a div in the nifty decelerating fashion we see on various sites. The kicker here is that the functions in the class that do the work on interval must be referenced using the Function.createDelegate() method or they won't work. The reason? Something known as closure, which is frankly too computer sciency for most people to be bothered with.

The same is true of using the callbacks for async calls using Sys.Net.WebRequest from within a class. For example, consider this block in a function defined in a Javascript prototype:

            var request = new Sys.Net.WebRequest();
            request.set_url(this._url);
            request.add_completed(Function.createDelegate(this, this.populateResponse));
            request.invoke();

PopulateResponse is another function defined in the prototype, and again, you need to wrap it in Function.createDelegate(). The long and short of this is that it exercises closure so that the function referenced in the delegate has context within the instance of the class you've created. If you read that Wikipedia link, you can see why closures make sense when dealing with asynchronous calls.

Serialization and you

Few things are as uninteresting as serialization, but when it isn't done efficiently, you get giant blocks of data being transported back and forth over the wire, slowing down your app. XML is sweet, but it can be a little on the heavy side for a lot of AJAX work. Fortunately, JSON is already Javascript, and it's a little less verbose than XML. By marking a WebService derived class with the ScriptService attribute, you're well on your way to JSON goodness coming down the pipe. When the WebService is referenced via the ScriptManager, it's as easy as calling the service by full name (because a proxy will be established by the ScriptManager), and then using the properties of the returned result object, to a degree, as if it were the same kind of container object on the server.

The stuff that's a little harder to get into

There is a lot of power I've yet to tap, not the least of which is more use of callbacks and delegates to orchestrate stuff happening between objects on the page. Granted, you need to have something in mind to start using these pieces (I do), but there's a lot of potential there to make everything easier to read and maintain.

Then there's the bit about Intellisense and script debugging in Visual Studio 2008. Frankly, the Intellisense is not all it's cracked up to be, and I find the scope of what VS "knows" about to be limited. I don't blame the IDE as much as I blame the fact that you can pretty much do whatever the hell you want in Javascript, whenever you want. The debugging is kind of a pain to, because for it to attach to the right processes, you have to run the app, which I rarely do because that's still a slow process. It's also not that useful when you're working on a page that's the product of URL mapping or some custom handler, or there's a query string on the page. I still find myself spending more time in Firebug, but maybe that's just because I don't use VS right.

Overall, I can't even tell you how excited I am to continue working with the AJAX framework. The more you use it, the more you see its potential. Much of it can't be realized by using the sparse and poor documentation online though. A good book really helps (like ASP.NET AJAX In Action, even though it does not cover WebMethods called within pages).
 

 

4 Comments

Comments have been disabled for this content.