Monday, November 15, 2010

Wicket-ing Ajax with jQuery

Being able to register ajax callbacks is a very common use-case in almost any web application. Chances that you would need a 'loading' ajax indicator when there is a task running in the background (gmail style) is very high.
This post is about how you can register ajax callbacks for wicket with jQuery.

In order to register ajax hooks with wicket, you have several options. Each option allows you to register one of the following three callbacks: before (executed right before the ajax call is made), after (when ajax response arrives) and failure (connection error or any other kind of ajax failure).

1) First option is to call window scoped functions, defined in wicket-ajax.js, like this:
function wicketGlobalPreCallHandler() {
  alert('Before ajax call');
}
function wicketGlobalPostCallHandler() {
  alert('ajax response ready');
}
function wicketGlobalFailureHandler() {
  alert('connection error');
}

This method is pretty straightforward, but it has one drawback: it doesn't allow to register more than one callback for each type of event.

2) Register callbacks using Wicket.Ajax utility methods.
When you want to register more than one hook, you can use do as follows:

(function() {
  //define callbacks
  var preAjaxHandler = function() {}
  var successHandler = function() {}
  var failureHandler = function() {}
  //register callbacks
  Wicket.Ajax.registerPreCallHandler(preAjaxHandler);
  Wicket.Ajax.registerPostCallHandler(successHandler);
  Wicket.Ajax.registerFailureHandler(failureHandler);
})();

This method allows you to register more than one callback for the same event. This is useful when, for instance, beside showing 'loading' ajax indicator, you want to do something else, like animating the updated by ajax DOM element.

3) The last option is similar to the previous one, but it is defined in jQuery style and hides the wicket related details.

/**
 * Helper for registerin ajax callbacks.
 * This plugin is dependent on jquery.namespace
 */
(function($) {
  $.wicket = {
    /**
     * Register a callback function to be executed after wicket ajax call is complete.
     */
    ajaxCallback : function(fn) {
      this.registerCallbacks({
        onSuccess: fn
      });
      return fn;
    },
    registerCallbacks: function(arg) {
      if ($.namespaceExist('Wicket.Ajax')) {
        if (arg.onBefore) {
          Wicket.Ajax.registerPreCallHandler(arg.onBefore);
        }
        if (arg.onSuccess) {
          Wicket.Ajax.registerPostCallHandler(arg.onSuccess);
        }
        if (arg.onFailure) {
          Wicket.Ajax.registerFailureHandler(arg.onFailure);
        }
      }
    }
  }
})(jQuery);

The above script depends on jQuery.namespace.js which defines $.namespaceExist method:
(function($) {
 $.namespace = function() {
  var a = arguments, o = null, i, j, d;
  for (i = 0; i < a.length; i = i + 1) {
   d = a[i].split(".");
   o = window;
   for (j = 0; j < d.length; j = j + 1) {
    o[d[j]] = o[d[j]] || {};
    o = o[d[j]];
   }
  }
  return o;
 };
 $.namespaceExist = function(funcName) {
  if (typeof(funcName) != "string") {
   return typeof(funcName);
  }
  var fnArray = funcName.split(".");
  var namespace = [];
  for (index in fnArray) {
   namespace.push(fnArray[index]);
   var token = namespace.join(".");
   if (eval("typeof " + token + " == 'undefined'")) {
    return false;
   }
  }
  return true;
 }
})(jQuery);
And below is a small usage example:
(function($){
  //define callbacks
  var preAjaxHandler = function() {}
  var successHandler = function() {}
  var failureHandler = function() {}

  //register success handler only
  $.wicket.ajaxCallback(successHandler);
  //register each event 
  $.wicket.registerCallbacks({
    onBefore: preAjaxHandler,
    onSucces: succesHandler,
    onFailure: failureHandler
  });
}(jQuery);

The advantage of this method is that you shouldn't worry if the wicket-ajax.js exist or not. The $.namespaceExist will take care about it.

A small note about $.namespace utility: it allows you easily define a sort of javascript 'packages'. The client code can look like this:

  (function($) {
    $.namespace('com.package.util', {
      computeX: function() {},
      computeY: function() {}
    });
    $.namespace('com.package.model', {
      name: function() {},
      age: function() {}
    });
    //calling defined methods:
    com.package.util.computeX();
    com.package.util.computeY(); 
  })(jQuery);

The plugin will take care of already defined namespace and will reuse it or will create it for you if it doesn't exist yet. This can be very useful utility when you want to break your application in several independent modules.

The code can be found on github repository at the following location: https://github.com/alexo/Javascript-Utilities/tree/master/jQueryPlugins/wicket/
Or you can simply checkout the project using the following url: git@github.com:alexo/Javascript-Utilities.git

2 comments:

  1. This is a very good example for me to get started.

    I tried with this it works
    great

    .loading-indicator-bars {
    background-image: url('images/loading-bars.gif');
    width: 150px;
    }


    now the problem I have is this showloading will appear for ajaxLazyLoadingComponent , is there a way I can avoid calling showLoading for ajaxlazyloading ?

    ReplyDelete
  2. what I meant is in function

    function wicketGlobalPreCallHandler(){

    }

    can I retrieve component information like class, if etc for which ajaxrequest is processing, I mean are there any get methods I can call inside this function ?
    the reason I want that information is to prevent showing any busy indicator some times

    ReplyDelete