Thursday, January 15, 2009

Ajax progress indicator with prototype

You may know that you can add a global ajax responder to your application:
Ajax.Responders.register({
onCreate: function() {
Ajax.activeRequestCount++;
},
onComplete: function() {
Ajax.activeRequestCount--;
}
});
But we not going to cover that here, because you can't get the originating object. For example, there's no way here for a global ajax responder to get the anchor object.
< a_class="add" href="#" onclick="new Ajax.Request('/products/3/categorizations/18', {asynchronous:true, evalScripts:true, method:'put'}); return false;">add
Here's the rails generator code:
<%= link_to_remote "add", { :url => product_categorization_path(@product, category), :method => :put } %>
So, let's add some code to prevent the user from clicking twice.
To keep the view clean, We'll implement it as a simple view helper in application_helper.rb
<%= link_to_remote "add", { :url => product_categorization_path(@product, category), :before => ajax_progress, :method => :put } %>

def ajax_progress
"setTimeout(function(){ this.innerHTML = '...' }.bind(this), 100)"
end
SetTimeout takes either a function name or an anonymous function. We binding the anonymous function to "this", which in this context refers to the 'A' anchor tag. Because it's bound, we can refer to "this" inside the function and get the anchor tag.

The setTimeout is useful because it lets us modify the tag (even remove it from the DOM) without messing with the ajax request. You can use setTimeout on a form to change the action, so it can't be submitted twice (for important forms)

We going to step it up one more, because this code belongs in a library. Time to open up application.js and create a ghetto pseudoclass singleton thingy.

This will prevent the link from doing anything if the user clicks it twice.

Now, to modify the ajax_progress helper to simple beauty.

def ajax_progress
"setTimeout(MyApp.ajaxing.bind(this), 100)"
end

Finally, let's show the user a GMail-style notice after 5 seconds, just to let them know that we're running slow or have just plain died.

MyApp = {

ajaxing: function(){
this.innerHTML = '...';
this.onclick = FacetApp.nothing;
setTimeout(MyApp.slooow.bind(this), 5000);
},

/* shows a ? symbol. useful for showing progress on an ajax action */
slooow: function(){
this.innerHTML = ".?."
/* show a warning message in the ui somewhere */
}
}

Got a better way of doing this?

http://www.caboo.se/articles/2008/3/27/ajax-progress-indicator-with-prototype




No comments: