<<  Change Style
  • Normal
  • |
  • Widen Column
  • |
  • Larger Text
Oct
09
Javascript Ajax - Writing Maintainable Code For Your Next Big Web Project

Before you turn your head and go away dismissing it as another noobie javascript how-to, imagine a situation where you have this big, enterprise-y web project and you have to do a lot of ajax interactions and calls at every page. You would say, "Oh! Very simple. I'll use jQuery.ajax with ajax options". Or something like new Ajax().request() if you're a Mootools developer. Basically, just one line of any library's ajax interface is all that you need to do the same. But take a look at this in the jQuery docs where it reads:

Add some extra simple arguments to $.ajax.
$.ajax(type, url, options);

Thats the jQuery roadmap for the 1.4 library which lists out the changes that may or may not be implemented in, i guess, the next jQuery release. But suppose its implemented and you have written $.ajax (the old version) at 50 different places in your web project codebase; what a pain it would be to change each one now. If that doesn't bother you, then you need not read further. If it does, then lets see how we can ease that pain without complicating or over-designing things.

Lets look straight at the code and the implementation/usage:

(function($, undefined){
    var cache = {};  // create a cache to store active ajax instances

/**
 * @param name - name of the ajax request running
 * @param type - type of request (GET, POST, PUT etc)
 * @param url - action url
 * @param options - a hash of options to create the ajax request. See $.ajax in api.jquery.com for the relevant options
 * @return XmlHTTPRequest object
 *
 * @usage To run one ajax request at one time, give the ajax method a name (which it assigns to the ajax object:
 *            doAjax('my_one_request', 'POST', '/action', { success : function(data){} });
 * @usage To run any number of requests at one time, don't give a name:
 *               doAjax('GET', '/action', { success : function(data){} });
 */

;var doAjax = function(){
    var a = arguments, l = a.length;

    // ajax constructor function
    function ajaxObj(type, url, options) {
        var defaults = {
                type : type || 'GET',
                url : url,
                success : $.noop
            };
        var settings = $.extend(defaults, options || {});
        
        // you can use any other js library's ajax interface here
        return $.ajax(settings);
    }

    if(l === 3) {
        return new ajaxObj(a[0], a[1], a[2]);
    } else {
        var name = a[0];
        if((typeof cache[name] !== "undefined" && cache[name].readyState == 4) || typeof cache[name] === "undefined") {
            cache[name] = new ajaxObj(a[1], a[2], a[3]);
        }
        return cache[name];
    }

};

})(jQuery);

There are two ways to use the above:

Usage 1:

doAjax('POST', '/action', 
            { data : serializedData,
               success : function(){ alert('It is done!') }
              // more ajax options -> see http://api.jquery.com/jQuery.ajax/
          });

Here i am sending three parameters (similar to the $.ajax syntax in the roadmap) which runs the following block of code:

if(l === 3) {
        return new ajaxObj(a[0], a[1], a[2]);
    }

This returns a new ajax (or XmlHTTPRequest) object that is created by the 'ajaxObj' constructor function.

 

Usage 2:

doAjax('loadComments', 'GET', '/action', 
            { beforeSend: function(xhr) {xhr.setRequestHeader("Accept", "text/javascript")},
               success : function(){ alert('Page has been fetched') }
          });

Now here i am sending four parameters where the first one is a string that assigns a name to the ajax call. This forces the browser to make a couple of checks before it creates a new ajax instance to respond to the request:

  • An instance with the same name exists in the 'cache' variable but is not running (i.e readyState = 4)
  • There is no instance with the given name in the 'cache' variable

The following line of code reflects the above 2 check cases:

if((typeof cache[name] !== "undefined" && cache[name].readyState == 4) || typeof cache[name] === "undefined") {
...

If a name exists, it returns the xmlHTTPRequest object stored in the 'cache' variable. This usage will be useful not just in POST requests, where you want the user to not make more than one request at a time, but particularly so in GET requests where you can prevent what i like to call an "ajax flash". This happens when you click on an ajax link ('link_to_remote' as rails guys would call it) multiple times in quick succession and the response's targeted section of the page looks like a fade-in-fade-out animation. Of course, there are cases such as search autocomplete where you would not want to send a name string as a parameter.

 

Now when the library code changes, all you need to do is make the change in the one line inside the 'ajaxObj' constructor, where i've used jQuery or any other library's low level ajax interface, and it will reflect in all the 50 places where you used 'doAjax'. This keeps your project resistant to library or api changes and upgrades. You might doubt, "Isn't it an unnecessary over-design, applying a wrapper over a piece of api code which is a wrapper in itself?" Well, if you don't think $.get, $.post, $.getJSON are over-designs, then no, this isn't one either (all of 'em use $.ajax).

Let me know your thoughts on this.

Short URL:

How about some bookmarking love?

Post To Twitter Bookmark on Delicious Bookmark on Digg Share on Facebook Update On Friendfeed Bookmark on Reddit Bookmark on Mixx
---
0 comments for this article ( 1 pending )

Leave a comment:

  • Captcha ImageRefresh Captcha