DWR Hooks and Dojo Dialogs

By on October 16, 2008 5:29 pm

It’s common to use Dojo to talk to a Java server using DWR. Here’s a handy hint for how to add a nice “loading” feature that uses a Dojo widget to display the progress made by a DWR call.

DWR has had a useLoadingMessage() function since version 1.0, but there have always been some problems with it – it mimics the early GMail loading message, which is OK if you like that style, but not otherwise. It can get confused if there are multiple actions outstanding, and there is no way to tell it to go away if you need to continue interacting with the page. We try with DWR to focus on remoting and not widgets, so we have not spent a lot of time on a fancy loading message widget.

Enter the Dijit Dialog which can solve all of these problems in addition to being accessible, themeable, localizable and generally more full of goodness.

Setting up a Dojo Dialog is simplicity itself, this should do the trick:

dojo.require("dijit.Dialog");

// The HTML content for the dialog, You probably want to customize this,
// maybe include a spinner from ajaxload.info or similar
var content = 'Loading data from server ...';

var loadingDialog = new dijit.Dialog({ title:"Loading", id:"loadingDialog" });
loadingDialog.setContent(content);
dojo.body().appendChild(loadingDialog.domNode);

What we need to do now is to display this dialog whenever some DWR communication is happening. DWR has hooks that you can register for just this purpose. The functions dwr.engine.setPreHook() and dwr.engine.setPostHook() are called before and after any remote call that DWR makes. The most basic implementation looks like this:

dwr.engine.setPreHook(function() {
    loadingDialog.show();
});
dwr.engine.setPostHook(function() {
    loadingDialog.hide();
});

One trick to be aware of is that since the calls are asynchronous, multiple calls can be outstanding at the same time, so simply closing the dialog whenever the post hook happens might be the wrong thing to do – we need to count the outstanding connections. This gives us the opportunity to give some extra feedback to the user about the number of things that we’re waiting for. Assuming that we’ve got some element to display the outstanding count called ‘loadingCount’, we could do this:

var outstanding = 0;

dwr.engine.setPreHook(function() {
  if (outstanding == 0) loadingDialog.show();
  outstanding++;
  dojo.byId('loadingCount').innerHTML = "" + outstanding;
});

dwr.engine.setPostHook(function() {
  outstanding--;
  if (outstanding == 0) loadingDialog.hide();
  dojo.byId('loadingCount').innerHTML = "" + outstanding;
});

So, putting it all together, adding to the dialog an indicator of how many requests are outstanding, and wrapping it all in a (function() {...})(); block to keep the global namespace clean, we get this:

dojo.require("dijit.Dialog");

(function() {
  var contentLine1 = 'Loading data from server ...
'; var contentLine2 = '(0 item(s) outstanding)'; var loadingDialog; var outstanding = 0; var initLoadingDialog = function() { loadingDialog = new dijit.Dialog({ title:"Loading", id:"loadingDialog" }); loadingDialog.setContent(contentLine1 + contentLine2); dojo.body().appendChild(loadingDialog.domNode); }; dwr.engine.setPreHook(function() { if (!loadingDialog) initLoadingDialog(); if (outstanding == 0) loadingDialog.show(); outstanding++; dojo.byId('loadingCount').innerHTML = "" + outstanding; }); dwr.engine.setPostHook(function() { outstanding--; if (outstanding == 0) loadingDialog.hide(); dojo.byId('loadingCount').innerHTML = "" + outstanding; }); })();

If you’re using DWR and Dojo, you should be able to chuck this code into your project somewhere for instant dialog base loading messages. The code as displayed will look something like this:

DWR+Dojo Loading Dialog