When faced with a new web project these days you typically hear the clients listing AJAX as one of the must haves in their brand new web application. Pretty cool as you might be accustomed yourself to AJAX to the extent that you can hardly imagine returning back to the page reload per click days. But, if you are more sensible (or better yet, your clients are so) you would think twice before entirely abandoning the normal site browsing model for an AJAX based one.
Why? I hear you say. Many reasons, including the fact that we live in the early 21st century, where – get ready for this – not all Internet access devices are equipped with state of the art browsers that can consume your AJAX interfaces or whatever Javascript or CSS magic you throw at them. Many mobile phones (millions to say the least) can hardly parse plain old HTML, some can do CSS but not Javascript
Ok, you tell me. “I will have to do two versions, one that is full of AJAX effects and one old boring HTML only version.” STOP IT, I say, you can't be more wrong. Thank God there could be more elegant solutions to the problem than just writing another application around the same database. I present to you my humble take on the problem. Using the Ruby on Rails Framework (you can apply similar thoughts in other frameworks if you like, and many ideas can be copied easily as they only involve Javascript)
First off, the controllers. The controllers are responsible for receiving requests and sending responses. What we need to do is make them intelligent enough to understand different types of requests and respond accordingly. This is done using Rails magical method “respond_to”
class IssuesController < ApplicationController
def index
...
respond_to do |format|
format.html { # do something }
format.js { # do another thing }
format.json { # and another thing }
format.xml { # ok, enough }
end
end
...
end
In the above example we see that each format will have a different response. This is great for a start, that way we can implement slightly varying responses for the AJAX and the none AJAX calls. To make things easier on us we will implement a very simple case of AJAX. Each rhtml view is rendered in a DIV tag within an rhtml layout. In the none AJAX model, pages are rendered by rendering both the layout and the inner view. In the AJAX model, only the inner view is rendered and is sent back to the browser to replace whatever resides in the content DIV.
So, our controllers will work as follows:
class IssuesController < ApplicationController
def index
...
respond_to do |format|
format.html # will render index.rhtml
format.js { render :layout => false }
# the above line will render index.rhtml but without the layout
end
end
...
end
The above lines made our controller ready to respond to normal or AJAX requests (given that AJAX requests will have the .js format). In the former case it will return back the whole page but in the latter it will omit rendering the layout and only send the content.
Ok, but what we still need two views. I hear you, and fear not, you will have to change nothing. Actually it's only a trivia to adapt your views to this model. Let's see how this can be done.
...
<div id=”content”>
...
<ul>
<li><a href=”url1”>Link1</a></li>
<li><a href=”url2”>Link2</a></li>
<li><a href=”url3”>Link3</a></li>
</ul>
...
<form target=”url4”>
...
<input type=”submit”>
</form>
...
</div>
...
The above fragment shows a list of links and a form. All should behave in the normal way and reload the page when clicked. Now let's imagine that the user is using a Javascript capable browser. What effect could this coming fragment have on his experience?
<!-- Warning, this fragment requires prototype.js -->
<script>
function ajaxifyLinks(){
// check if there is AJAX support
if(!Ajax.getTransport())return false;
// loop on all links
$$('a').each(function(link){
// attach an event observer to each link's 'onclick' event
Event.observe(link, 'click', function(event){
// call the original url (with .js added) with AJAX
new Ajax.Updater('content',link.href+”.js”);
// stop the browser from following the link
return false;
});
});
// loop on all forms
$$('form').each(function(form){
// attach an event observer to each form's 'onsubmit' event
Event.observe(form, 'submit', function(event){
// send the form contents via AJAX
new Ajax.Updater('content',form.action+”.js”,
{params:Form.serialize(form),
method:'post'});
// stop the browser from submitting the form
return false;
});
});
}
</script>
The above code will transform EVERY link and form in the page to AJAX, that is, in case that the browser supports both Javascript and AJAX. Otherwise links and forms will remain untouched and they will behave as usual.
Of course this is a minimalistic example. We knowingly avoided touching on any special case but, in another installment of this article we will get more intimate with the subject and may be we can handle more aggressive ... techniques!