Writing AJAX-style web applications can be very tedious. If you're using XML as your transport layer, you have to parse the XML before you can work with it. It's a bit easier if you're using JSON, but once you have parsed the data, the data still needs to be turned into HTML markup that matches the current markup on the page. Finally, the newly created markup needs to be inserted into the correct place in the DOM, and any event handlers need to be attached to the appropriate newly-inserted markup.
So there's the parsing, the markup assembly, the DOM insertion, and finally the event handler attachment. Most of the time, people tend to write custom code for each element that needs asynchronous updating. There are several drawbacks with this scenario, but the most frustrating part is probably that the presentation logic is implemented twice--once in a templating language on the server which is designed specifically for outputting markup, and again on the client with inline Javascript. This leads to problems both in the agility and in the maintainability of this type of application.
With flojax, this can all be accomplished with one generalized implementation. The same server-side logic that generates the data for the first synchronous request can be used to respond to subsequent asynchronous requests, and unobtrusive attributes specify what to do for the rest.
The Basics
The first component for creating an application using the flojax strategy is to break up the content that you would like to reload asynchronously into smaller fragments. As a basic example of this, let's examine the case where there is a panel of buttons that you would like to turn into asynchronous requests instead of full page reloads.
The rendered markup for a fragment of buttons could look something like this:
<div class="buttons">
<a href="/vote/up/item1/">Vote up</a>
<a href="/vote/down/item1/">Vote down</a>
<a href="/favorite/item1/">Add to your favorites</a>
</div>
In a templating language, the logic might look something like this:
<div class="buttons">
{% if voted %}
<a href="/vote/clear/{{ item.id }}/">Clear your vote</a>
{% else %}
<a href="/vote/up/{{ item.id }}/">Vote up</a>
<a href="/vote/down/{{ item.id }}/">Vote down</a>
{% endif %}
{% if favorited %}
<a href="/favorite/{{ item.id }}/">Add to your favorites</a>
{% else %}
<a href="/unfavorite/{{ item.id }}/">Remove from your favorites</a>
{% endif %}
</div>
(Typically you wouldn't use anchors to do operations that can change state on the server, so you can imagine this would be accomplished using forms. However, for demonstration and clarity purposes I'm going to leave these as links.)
Now that we have written a fragment, we can start using it in our larger templates by way of an include, which might look something like this:
...
<p>If you like this item, consider favoriting or voting on it:</p>
{% include "fragments/buttons.html" %}
...
To change this from being standard links to being asynchronously updated, we just need to annotate a small amount of data onto the relevant links in the fragment.
<div class="buttons">
{% if voted %}
<a href="/vote/clear/{{ item.id }}/" class="flojax" rel="buttons">Clear your vote</a>
{% else %}
<a href="/vote/up/{{ item.id }}/" class="flojax" rel="buttons">Vote up</a>
<a href="/vote/down/{{ item.id }}/" class="flojax" rel="buttons">Vote down</a>
{% endif %}
{% if favorited %}
<a href="/favorite/{{ item.id }}/" class="flojax" rel="buttons">Add to your favorites</a>
{% else %}
<a href="/unfavorite/{{ item.id }}/" class="flojax" rel="buttons">Remove from your favorites</a>
{% endif %}
</div>
That's it! At this point, all of the click events that happen on these links will be changed into POST requests, and the response from the server will be inserted into the DOM in place of this div with the class of "buttons". If you didn't catch it, all that was done was to add the "flojax" class onto each of the links, and add a rel attribute that refers to the class of the parent node in the DOM to be replaced--in this case, "buttons".
Of course, there needs to be a server side component to this strategy, so that instead of rendering the whole page, the server just renders the fragment. Most modern Javascript frameworks add a header to the request to let the server know that the request was made asynchronously from Javascript. Here's how the code on the server to handle the flojax-style request might look (in a kind of non-web-framework-specific Python code):
def vote(request, direction, item_id):
item = get_item(item_id)
if direction == 'clear':
clear_vote(request.user, item)
elif direction == 'up':
vote_up(request.user, item)
elif direction == 'down':
vote_down(request.user, item)
context = {'voted': direction != 'clear', 'item': item}
if request.is_ajax():
return render_to_response('fragments/buttons.html', context)
# ... the non-ajax implementation details go here
return render_to_response('items/item_detail.html', context)
There are several advantages to writing your request handlers in this way. First, note that we were able to totally reuse the same templating logic from before--we just render out the fragment instead of including it in a larger template. Second, we have provided a graceful degradation path where users without javascript are able to interact with the site as well, albeit with a worse user experience.
That's really all there is to writing web applications using the flojax strategy.
Implementation Details
I don't believe that the Javascript code for this method can be easily reused, because each web application tends to have a different way of showing errors and other such things to the user. In this post, I'm going to provide a reference implementation (using jQuery) that can be used as a starting point for writing your own versions. The bulk of the work is done in a function that is called on every page load, called flojax_init.
function flojax_clicked() {
var link = $(this);
var parent = link.parents('.' + link.attr('rel'));
function successCallback(data, textStatus) {
parent.replaceWith(data);
flojax_init();
}
function errorCallback(request, textStatus, errorThrown) {
alert('There was an error in performing the requested operation');
}
$.ajax({
'url': link.attr('href'),
'type': 'POST',
'data': '',
'success': successCallback,
'error': errorCallback
});
return false;
}
function flojax_init() {
$('a.flojax').live('click', flojax_clicked);
}
There's really not a lot of code there. It POSTS to the given URL and replaces the specified parent class with the content of the response, and then re-initializes the flojax handler. The re-initialization could even be done in a smarter way, as well, by targeting only the newly inserted content. Also, you might imagine that an alert message probably wouldn't be such a great user experience, so you could integrate error messages into some sort of Javascript messaging or growl-style system.
Extending Flojax
Often times you'll want to do other things on the page when the asynchronous request happens. For our example, maybe there is some kind of vote counter that needs to be updated or some other messages that need to be displayed.
In these cases, I have found that using hidden input elements in the fragments can be useful for transferring that information from the server to the client. As long as the value in the hidden elements adheres to some predefined structure that your client knows about (it could even be something like JSON if you need to go that route).
If what you want can't be done by extending the fragments in this way, then flojax isn't the right strategy for that particular feature.
Limitations
This technique cannot solve all of the world's problems. It can't even solve all of the problems involved in writing an AJAX-style web application. It can, however, handle a fair amount of simple cases where all you want to do is quickly set up a way for a user's action to replace content on a page.
Some specific examples of things that flojax can't help with are if a user action can possibly update many items on a page, or if something needs to happen without a user clicking on a link. In these situations, you are better off coding a custom solution instead of trying to shoehorn it into the flojax workflow.
Conclusion
Writing AJAX-style web applications is usually tedious, but using the techniques that I've described, a large majority of the tedious work can be reduced. By using the same template code for rendering the page initially as with subsequent asynchronous requests, you ensure that code is not duplicated. By rendering HTML fragments, the client doesn't have to go through the effort of parsing the output and converting the result into correct DOM objects. Finally, by using a few unobtrusive conventions (like the rel attribute and the flojax class), the Javascript code that a web application developer writes is able to be reused again and again.
I don't believe that any of the details that I'm describing are new. In fact, people have been doing most of these things for years. What I think may in fact be new is the generalization of the sum of these techniques in this way. It's still very much a work in progress, though. As I use flojax more and more, I hope to find not only places where it can be extended to cover more use cases, but also its limitations and places where it makes more sense to use another approach.
What do you think about this technique? Are you using any techniques like this for your web applications? If so, how do they differ from what I've described?
A few months back, I had a bit of an epiphany. I suspect I was about 5 years too late with this epiphany, but nevertheless here it is: Microsoft, Sun, and Adobe all have these virtual machines that they want everyone to develop on top of. For Microsoft it's the CLR, for Sun it's the JVM, and for Adobe it's AVM. The problem that each of them have is that not everyone goes out of their way to install all of these VMs, so each vendor only has a subset of the entire computing space.
My epiphany was that there is one VM which nearly every modern computer has access to. It's JavaScript. Every browser developed in the past 10 years ships with some variation of it, and some systems even come with it as a command-line option. Whilst they do have a big problem in terms of standardization (or lack thereof), there is certainly a lowest common denominator that could be useful for writing apps on top of.
That being the case, why don't we see more language implementations on top of JavaScript? There is Processing.js, a port of the Processing programming language. There is also Objective J and Cappuccino, a port of Objective C and Cocoa, respectively. Each of those language implementations have received quite a bit of attention (both positive and negative, mind you).
So why don't we see Ruby, Python, or other languages implemented on top of JavaScript? For that matter, why don't we see more ports of apps to JavaScript? I know that in the Python world, PyPy has a JavaScript backend, but to the best of my knowledge that backend has been all-but-abandoned. I think it would be really cool if we were to see more applications and programming languages targeting the most widely adopted virtual machine ever: JavaScript.
It seems that the world of computing is always oscillating between offloading more work to the server, and offloading more work to the client. In the very early days of dumb terminals, we had most of the actual computation being done on big mainframes. Then personal computers became more powerful, allowing for much richer applications to be created.
With the advent of the internet, the landscape of computing architectures shifted again to being done focused on the server. Now, however, JavaScript has become more powerful. Combined with things like SVG and the canvas tag, we can create extremely rich applications that take place solely in the browser.
Projects like CouchDB are even starting to open the door for truly peer-to-peer web applications. With all of this taking place, it seems that client-side web applications are poised to see some fairly strong growth. This is especially evident now that companies as big as Google seem interested in the idea, with its Google Gears product that allows you to "work offline". But there are certain things that need to be satisfied first.
We need a way of enforcing security across these apps. It looks like some combination of OpenID and OAuth are going to be the winners in this space, but I've never seen a seamless implementation of either of these protocols, even by the companies most invested in the technology. There is a lot of work to go on usability before authentication and authorization are ubiquitous through these open protocols.
We also need to standardize more on the data interchange formats that we use to shuttle information back and forth between these different apps. Atom goes a long way towards describing the data that we use, but its adoption is nowhere near ubiquitous, and some sites still rely on older, more outdated, RSS syndication formats that aren't quite up to the task.
But even if we standardize on some application platform (be it Google Gears or CouchDB or some other container), security, and data interchange formats, there are certain things that need to be considered. For one, there are some applications that just aren't practical to be implemented on the client. Video editing comes to mind (and I would know, considering I interned for eyespot, a company which was attempting to do just that).
Another concern is that, as we've seen with the emergence of standards for CSS and HTML, a certain amount of rigidness is good, but a strict conformist attitude leads to significantly stifled innovation. If you were to write an app that doesn't fall within the boundaries of what's possible given the agreed-upon standards, would you still be able to go forward with the development of the app, or would you run into resistance from those who have a stake in those standards?
In all, I have a feeling that we are going to move more and more to a hybrid approach, with much more logic being computed on the side of the client (especially in terms of visual components and interactivity), and that much more of the server side is going to be involved in slicing and serving up just the raw data. We can see this happening today with technologies like AJAX being touted as the centerpiece of some "Web Two Point Oh" sites. I'm excited to see where this will all go, and more than excited that, being a developer during this time, get to help shape that direction.
Recently I've been using a lot of FriendFeed lately, and found it a bit annoying to expand comments one-by-one, so I wrote a quick bookmarklet to automatically expand all comments out.
Drag this bookmarklet to your bookmarks toolbar to get in on the action:
By the way, feel free to follow me on FriendFeed as well to see my various online activities. I'm in the habit of following people who follow me.
It's an interesting feeling open-sourcing an application that you've developed for your own purposes. Will people use it? Will people find major design flaws? Is it just a big waste of time? These were the questions that were going through my mind before open-sourcing django-threadedcomments. Fortunately, my worries were quelled almost instantly as a people reacted quite positively to the application. There was one recurring comment, however, that almost everyone who tried the sample implementation said: those colors are hideous.
Beyond even that though, it seems that people saw the sample implementation and got the impression that django-threadedcomments = that sample implementation. To me, that's an underestimation of the power of the modular django app. I think that James Bennett hits the nail on the head when he says that:
"Rather than a single definitive 'Django blog' application, for example, I think it’s much more likely we’ll see a collection of applications which, taken together, provide all the key functionality..."
It was this sentiment that pervaded nearly all of the design decisions behind django-threadedcomments: it should be flexible, modular, and reusable so that, taken together with other similarly-designed apps, it can provide some compelling functionality at a fraction of the effort. Now we most assuredly didn't achieve all of those design goals fully, but I believe we're headed in at least the right direction with its development.
To prove my point, I decided to create an improved Digg/Reddit comment system clone using Jonathan Buchanan's wonderful django-voting application, alongside django-threadedcomments, and a fair bit of jQuery. Being almost completely new to Javascript, I was pleasantly surprised by how easy it was to not only integrate all of these technologies and use extensive client- and server-side scripting, but also to achieve a compelling commenting system in well under a week of spare time. Oh, and this time there was actually some effort in making the look and feel of the commenting system acceptable! As with the first example, this one is completely open sourced and available in the django-threadedcomments SVN repository.
Without further ado, the Example Digg/Reddit Comment Clone Plus Focus.
All Content

