Recent Weblogs

Links I like

Ajax and its Alzheimers


The demonstration here is showing that the service retrieves a word by a simple index(number), that word gets returned in english then sent off to three different translators. The retrieval of a new word is considered a new 'state' and thus should be reconstructed when saving in a bookmark or when traversing the browser's history.

Ajax is a great tool to build web widgets and auto completes etc. But when getting into larger scale applications. Where an application takes different states things aren't so easy. An 'Ajax' aka XHR request doesn't trigger any sort of browser history mechanisms. Which is why the whole 'behind the scenes' thing works so well. Lets take a mail application built with ajax, so the page loads and they need to search for a particular email. So they go into the search page, type in a query, submit it, then choose the email from the result. At this point our user has navigated through four essential states.

  1. The initial load, inbox.
  2. The base search page.
  3. The search results page.
  4. The email of choice.

If the user at this point realizes they've chosen the wrong email, clicking back should bring them back to the search results page. But if this mail application took no accomodations for history control then the window either will take them back to their previous page or simply won't allow the user to click the back button.

Back and Forth, History Traversal

What needs to happen is trigger the browser to register a new history index in the window's history without reloading the page. This can be done by using 'internal links'. The problem with this is though it doesn't recreate our javascript manipulated DOM, we have to handle all of that ourselves. Worse yet it doesn't throw an event for this action either. We can catch it when we're the ones adding the parameters to the URL, but we have no idea when a user uses the history traversal controls(back and forth buttons) so unfortunately we have to use a polling technique. Keeping the operations in the polling function to a minimal is ideal. In the AjaxHistory class it only does a simple string comparison, when the strings are different is when it throws the event that the state of the application should change.

Time for Class, a visit to the AgileAjax Library

The three major classes in the library.

  • EventDispatcher, a class to inherit from to do class based events.(Non-DOM events). This is how the two other classes communnicate, avoiding direct method calls, leading to tight coupling. Also due to the nature of asynchronous requests, the event driven communication is much more ideal.
  • AjaxService, the bread and butter of the library, handles all the inconviences of the XHR as well as adding much needed functionality, such as response caching, request timeouts, a much better event system and of course, history control.
  • AjaxHistory, the history component that serves AjaxService, throws an event when the user has accessed the history traversal controls.

Previous work on this problem

  • AjaxService -- the initial attempt at consolidation of XHR commmunications.
  • AjaxHistory -- the first try at managing history controls
  • Ajax bookmarking -- producing stateful applications, such that an ajax derived state can be restored with a URL
  • Agile Ajax -- my first article on the topic of a cohesive single stand alone library for tackling XHR inconsistencies as well as the history problem.
  • Ajax Timeout -- discussing the needs for timeout functionality with an ajax request. Also making the neccessary modifications to the prototype Ajax.Request class to accomodate timeout functionality.

Other References

  • DHTML Nirvana an interesting approach using IFRAMES, it had some shortcomings for me in FF 2.0 but a good effort at the problem.
  • Brad Nuremburg describes a method using window.location, a polling interval and a structured HashMap object.
  • Aaron Swartz talks about the issue, wasn't able to find his implementation or a demo of his approach in action.

The Code

Just to give you an idea of how easy the AgileAjax library makes XHR requests, here is the code I used to implement the demo on this page. Granted I used Prototype for the DOM traversal/manipulation which I do love, but still, enabling history is as easy as that service.enableHistory and now your ajax requests now register with the browser's history module.

google.load("language", "1");

	

window.onload = function(){
	function handleSuccess(xhr){
		
		var obj = eval('(' + xhr.responseText + ')');
		$$('.word').invoke('update', obj.word);
		tService.translate(obj.word, 'en', 'de', Prototype.K);
		tService.translate(obj.word, 'en', 'el', Prototype.K);
		tService.translate(obj.word, 'en', 'hi', Prototype.K);
	}
	function handleTranslation(obj){
		$$('*[lang='+obj.destination+']').invoke('update', obj.translation);
	}
	function handleException(){
		console.log('wtf happen?! %o', arguments);
	}
	var service = new AjaxService('ajax.php');
	var tService = new GoogleTranslatorService();
	
	service.on('success', handleSuccess);
	service.on('exception', handleException);
	
	tService.on('translation', handleTranslation);
	service.enableHistory();
	service.enableCache();
	
	$$('.action').invoke('observe', 'click', function(e){
		service.send({number : Math.round(Math.random() * 10)});		
	});
}

Comments are Disabled.