Tuesday, October 13, 2009

Refreshing Data in the UI

Once your application is up and running smoothly, all the data appears as it should, it may seem that all there is left to do is style it up and move on. But in high volume sites, or low volume sites with areas of high transaction, you need to ensure the user is seeing the latest information available. This needs to be done via XMLHttpRequests so that the page that the user is working on remains intact.

Lets first discuss the data is stored and retrieved.

In any web application, the user will request data and you present it. Often the state of the page is determined by the data that is present. So you want to limit the number of calls to the database for this information by caching it in your backing bean. This requires that you reset this data when appropriate. Once the page is loaded, the data remains flat on the page until the user interacts, or you cause an update. Thus we load the data on demand, or lazily, and then cache it until it needs refreshing.

Refresh from the UI is made by AJAX calls. The calls invoke backing bean actions based on the framework you are using. Apache Trinidad has a nice built in mechanism for making AJAX calls to the JSF.

In my backing beans, I add a PollListener method that resets the data I want to refresh in the UI. The method looks like this:

public void tableDataPoll(PollEvent ev) {
resetData();
}


When the bean gets the call from the AJAX event, it resets the underlying data so that when the page requests the data, it is pulled afresh. In the UI the call is made by adding a trinidad poll ( An AJAX call that runs at a certain interval) to the page:

tr:poll interval="30000" pollListener="{bean.tableDataPoll}" id="ajax1"

This causes an AJAX call to invoke my poll listener in the backing bean every thirty seconds. In order for the table of data to update, you need to add a partial trigger to the component that displays the data. This is done as follows:

tr:table value="{bean.backingData}" partialTriggers="::ajax1"

So the poll initiates an ajax call to the bean, resets the backingData in the bean, and the table refreshes by updating itself with the new value for the backing data.

It is incredibly easy to do.

On another project where I did not use Trinidad, we achieved the same by writing out the ajax calls. Basically, the pages would call your ajax javascript library with the ids of the objects to update, and your backing bean would change these values. I found that an extremely useful tool was a window within the page that showed the ajax calls. In the page, I added a div element such as:

div id = "div_ajax_log"
textarea id = "log_ajax" name = "status" rows = "25" cols = "55"
/div

I would have a javascript library for ajax, and in this library I would have a toggle to turn ajax logging on and off. I would also set the style for the div as none or block based on wheterh I was in debug /dev mode or not. That way if there was an issue in production, I could inspect the messages coming to and from the server to the page.

In my js file for ajax, I would specify whether we are logging, and the element to log to :
// Whether to log
var _logging = false;
// Where to log
var _log_elem;

I would then specify a logging function:

function logger(text, clear) {
if(!_log_elem) {
_log_elem=document.getEleemntById("log_ajax");
if(clear)
_log_elem.value="";
var old = _log_elem.value;
_log_elem.value = text + ((old) ? "\r\n" : "") + old;
}

You will need to add in some null checks and/or try blocks in the above basic version.

Then, in my ajax methods I would check to see if the logger was running and if so spit out information to the text area. For outgoing calls:

logger("AJAX Request: " + ((async) ? "Async" : "Sync") + " " + method + ": URL: " + url + ", Data: " + data);

Where the variables were passed in by the calling page. For outgoing responses, I would do the same - here the variable AJAX is the XMLHttpRequest generated in the original call.

logger(AJAX.status);

logger(AJAX.responseText);

The result is a conditional logger to the page with the information carried to and from the page in AJAX calls.

Hope that helps.