Handling Error Feedback from Ajax Requests to Rails Applications

November 17, 2008 · 1 min read

Ajax is frequently used to deliver a richer user experience. So why are error messages so rarely handled properly in Ajax-enabled applications? Handling errors gracefully -- in a way that actually helps the visitor fix the problem -- adds a genuinely high-quality feel. We've already got all the machinery we need. It just takes a little care and attention.

class FoosController < ApplicationController
  def update
    @foo = Foo.find(params[:id])
    respond_to do |format|
      if @foo.save
        format.html do
          flash[:info] = "Your foo has been created."
          redirect_to @foo
        end
        format.js { head :ok }
      else
        format.html do
          flash.now[:warning] = "I could not update the foo."
          render :action => :edit
        end
        format.json do
          head :unprocessable_entity, :json => @foo.errors.to_json
        end
      end
    end
  end
end

With this controller, you get a solid fallback for standard HTML requests and clean JSON behaviour for Ajax. When something goes wrong on a JSON request, you get back an array of arrays that looks like this:

[
  [ "attribute1", "error1", "error2" ],
  [ "attribute2", "error3" ]
]

Think of the things you can do with that kind of structured feedback:

new Ajax.Request('/foo.json', {
  method: 'PUT',
  parameters: {
    authenticity_token: window._token,
    "foo[subject]": $F('foo_subject'),
    "foo[body]"   : $F('foo_body')
  },
  onSuccess: function(transport) {
    // This is Web 2.0: celebrate with a yellow highlight.
  },
  onFailure: function(transport) {
    var errors = transport.responseJSON;
    errors.each(function(error) {
      var attribute = error.shift();
      var messages = error.join(", ");
      var errorMessage = attribute + " " + messages;
      var inputNode = $("foo_" + attribute);
      if(inputNode) {
        // Show that something is wrong with this field.
        inputNode.addClassName("error");
        // Do something better than an alert box. Alert boxes suck.
        alert(errorMessage);
      }
    });
  }
});

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.