I used to brush aside UJS as unnecessary and a source of maintenance problems because of the disconnect from the HTML. Developers coming in with no knowledge of the JS will have problems debugging complex JS that’s been unobtrusively created. I do still feel this is true but the benefit of UJS is in the simplicity and elegance of your JS code in the end. It might add a layer of complexity because of the detatchment from the HTML but the JS you end up developing will be cleaner, more concise, and ultimately much much more readable than embedding it into the HTML.
With that rant out of the way, lets examine a problem with the Prototype library. When doing Ajax calls we can set onSuccess, onFailure, and a few other observers. With UJS your Ajax observers might look something like this:
$('something').observe('ajax:success', function(right_evt) {
alert('success');
});
$('something').observe('ajax:failure', function(right_evt) {
alert('success');
});
But what if our Rails app makes smart use of HTTP status codes and returns status codes like 401 or 302? With Prototype, we can specify on401 or on302 but the catch all observers like onFailure won’t get called in those cases. For instance, just adding the following to the above code:
$('something').observe('ajax:on401', function(right_evt) {
alert('unauthenticated');
});
Has 2 problems. First, it might seem like this is possible with Prototype the Rails UJS, rails.js, doesn’t actually recognize any specific status code observers. So that on401 will just get ignored. There are 2 solutions to this. The first is to hack up rails.js and add a listener for on401 or whatever other status codes you want to specify. The problem is Prototype will ignore the other catch all observers like onFailure if you start specifying specific status codes. A better solution is to just catch the specific status codes that you want custom inside of the catch all onFailure observers. You can do that like this:
$('something').observe('ajax:failure', function(evt) {
if(evt.memo.status == 401) {
alert('unauthenticated');
return false;
};
alert('Failure');
});
Now this will handle 401s differently than other failures but still catch all the other failures without having to specify all of the status codes individually.