Rails Views, Avoiding AJAX Faux Pas
When developing applications that make use of AJAX, I try to remember one simple rule.
1. Don’t leave the user hanging
There’s nothing more annoying to a user than not knowing what’s going on. If you don’t keep the user informed, your application will seem less, rather than more, comfortable than a traditional web interface. After all, when a browser is changing locations it at least has the common decency to show that something is happening– and a new page is displayed when it’s done.
If your users are confused and have no idea what’s going on– if they’re not even sure your application is working– have you really taken a step forward by using AJAX… or are you just scratching a technical itch and doing your users a disservice in the process? This is about user happiness, not cool technologies.
Here’s some basic ground rules for interacting with users during remote actions.
- You will always indicate when a remote action is occurring. Show a progress indicator; spinners are common these days, but use whatever makes sense. And don’t be fooled into thinking the action occurs so fast no indicator is needed– it may seem nearly instantaneous on your development server (with just you accessing it), but who knows what the future will hold?
- You will always stop the progress indicator when the action is complete. An ever-spinning progress indicator is pretty, but probably pretty confusing, too.
- If the action results in success, you will always show the result. Reload a div, insert an element, maybe do some fancy scriptaculous “Hey, look at me” effects. The point here is to do something. The user did something, and they want something in return.
- If the action results in failure, you will always notify the user. The only thing more frustrating than not knowing that something is going on is not knowing it failed– or how to fix it.
So, those are the rules. How do we meet them without too much fuss?
First of all, let’s build a little helper method to help show and hide our progress indicators (setting the :loading and :complete options). A simple implementation might look something like:
def showing_progress(opts={})
opts.merge :loading => update_page{|p| p[:progress].show },
:complete => update_page{|p| p[:progress].hide }
end
And, using it:
<%= link_to_remote "Do Something Remotely", showing_progress(:url=>some_url) %>
So, that leaves indicating success and failure– and here’s one approach. I’m not a big fan of using the :success and :failure callbacks on the link/form. I think it’s messier and less flexible to handle these events on the client-side than it is for the action, generating an appropriate RJS response for either outcome.
So, it all comes down to rendering the result of our remote action. If there’s been errors, we need to report them in a standard way– but if everything is okay, we need to have the flexibility to update the page in whatever manner makes sense for that action.
A good way to do this is to wrap render :update to handle both cases. Here’s an example with some pretty simplistic error reporting. This can be used from any controller action (put it in application.rb).
def reporting_errors_on(*objs)
valid = objs.all?{|o| o.valid?}
render :update do |page|
if valid
yield page
else
flash[:error] = objs.map{|o| o.errors.full_messages}.flatten.join("\n")
page.alert flash[:error]
end
end
end
So, if any of the ActiveRecord objects passed into the method are invalid, the errors are reported. Otherwise, a javascript generator object is yielded to the block, and we can use it however we’d like. (Note: The errors are also stored in flash, though that’s primarily for functional testing purposes)
Here’s an example of how to use it:
def add_item
item = Item.create(params[:item])
reporting_errors_on item do |page|
# Success; only happens if no errors are present
page[:items].reload
page[item.dom_id].visual_effect :highlight
end
end
Obviously, these aren’t the only ways to handle indicating progress and notifying the user of errors. These are also fairly simplistic examples; you might want to show text with your progress indicator, for instance, or show which fields are invalid instead of just popping up an alert box.
The point here is to keep your users in the loop– at a minimum of pain and suffering to yourself. As in any relationship, communication is key… don’t make the mistake of neglecting your users.
References: (for the use of ActiveRecord::Base#dom_id)






