post icon

Handling Validation Errors using Ajax Requests in Rails

After spending about 6 hours reserching how to handle errors when using ajax requests to create, update and do other actions in Ruby on Rails I got fed up and started doing it on my own, with a little help from Agile Web Development with Rails.

I spent countless hours looking through google and found nothing I liked, and this is why I’m writing this post. I also found nothing that showed all the code, from view to controller to js. So I’ll try and include everything here.

First background on what I wanted to do… I have an index.html.erb file that loops through an object that contains a bunch of stuff. What I wanted was:

  1. Have a form_remote_for at the bottom of this list that would do an Ajax.Request
  2. Have the new form values save to the database
  3. Print the new data to the bottom of the current list
  4. And handle any errors so the user would know wtf was going on

Ryan Bates has a sweet railscast screencast showing how to implement… well everything except the error handling. I’m just going to make it really simple just so you get an idea.

My index.html.erb file:

<table id="teams">
 <tr>
  <th>Team name</th>
  <% if logged_in? -%>
   <th>Action</th>
  <% end -%>
  </tr>
  <% for @team in @teams -%>
   <tr>
    <td><%= link_to @team.name, league_team_path(@league.id, @team.id) %></td>
    <% if logged_in? -%>
     <td>
      <%= link_to_remote image_tag('file_remove.png'), :url => team_path(@team.id), :method => :delete, :confirm => "Are you sure?" %>
      <%= link_to image_tag('file_edit.png'), edit_team_path(@team.id) %>
     </td>
    <% end -%>
   </tr>
  <% end -%>
</table>
<% if logged_in? -%>
 <div class="ajax_form">
  <% form_remote_for :team, :url => teams_path do |f| -%>
   <p id="team_name_para">
    <label id="team_name_label" for="team_name">Team name:</label>
    <%= f.text_field :name, :class => "large_set_width", :value => "" %>
    <button class="form_submit" type="submit"><span>Save</span></button>
   </p>
  <% end -%>
 </div>
<% end -%>

This is pretty basic, I just loop through all teams and print them out into a table with admin actions to edit and delete. Then the form_remote_for at the bottom that sends an Ajax.Request

My teams_controller.rb create action:

def create
 @team = @league.teams.build(params[:team])
 if @team.save
? respond_to do |format|
?? flash[:notice] = @team.name + ' was successfully created.'
?? format.html { redirect_to teams_path(@team) }
?? format.js {
??? render :update do |page|
???  page.insert_html :bottom, :teams, :partial => "partials/assets/team", :object => @team
???  page.replace_html :notice, "<p>"+flash[:notice]+"</p>"
???  page.show 'notice'
???  page.visual_effect :highlight, 'notice'
??  end
?  }
? end
 else
  respond_to do |format|
?  format.html { render :action => "new" }
?  format.js {
??  render :update do |page|
???  page.visual_effect :shake, "team_name_para"
???  page.replace_html :team_name_label, @team.errors.full_messages
???  page << "$('team_name_para').addClassName('form_error_message');"
??  end
?  }
? end
 end
end

This is pretty basic as well, we have the usual if @team.save and the html format in the respond to. What you want to look at is the format.js block. Instead of rendering a rjs file, were just going to take care of everything right in the controller. The first page.insert_html inserts the content from the team partial that gets the @team object passed to it. Partial looks like this:

<tr>
 <td><%= link_to @team.name, team_path(@team.id) %></td>
 <% if logged_in? -%>
  <td>
   <%= link_to_remote image_tag('file_remove.png'), :url => team_path(@team.id), :method => :delete, :confirm => "Are you sure?" %>
?? <%= link_to image_tag('file_edit.png'), edit_league_team_path(@league.id, @team.id) %>
  </td>
 <% end -%>
</tr>

So basically that just renders to the bottom of the table… Then the next 3 just handle the flash[:notice]. I put in some scriptaculous goodness as well. One issue I had was the flash[:notice]. In my application.html.erb layout I had:

<% if flash[:notice] -%>
 <div id="notice">
? <%= flash[:notice] %>
 </div>
<% end -%>

But this wasn’t going to work with the page.replace_html :notice because the div didn’t exist yet until the flash was created. So I switch things up and applied css to the paragraph tag instead of the div:

<div id="notice">??? ??? ??? ??? ??? ???
?<% if flash[:notice] -%>
? <p><%= flash[:notice] %></p>
?<% end -%>
</div>

Now for the fun part… err umm not so fun error handling. You’ll notice that the if @team.save has an else like usual. So we can do format.js like above. First if there were errors I would shake the entire paragraph tag that contains the label, input and button. This is some more scriptaculous goodness and really tells the user there’s something wrong. Next because we are only working with one input we can replace the html inside the label with @team.error.full_messages and add a class name so we can turn it red so they know there’s an error on that field.

That’s it!! Pretty simple stuff. Also if you have something more complex with multiple input and fields we can handle the errors a bit differently. First you can put an empty div on your page somewhere:

<div id="ajax_form_errors"></div>

Then in the else statement just add this into the format.js block:

page.replace_html :ajax_games_form_errors, :partial => "partials/assets/errors"

Then the errors.html.erb partial:

<div id="errorExplanation" class="errorExplanation">
 <h2><%= @game.errors.count %> errors prohibited this game from being saved</h2>
?<p>There were problems with the following fields:</p>
?<ul>
? <% @game.errors.each_full do |msg| -%>
?? <li><%= msg %></li>
? <% end -%>
?</ul>
</div>

What were doing here is basically just the same thing ActiveRecord::Errors does with it default setup for error handling. Looping over the errors and printing them out in a list item.

That’s it that’s all. Lemme know if you have any problems and I’ll try and help you out the best I can.

14 Comments

Leave a comment
  1. oli
    24. Jul, 2009 at 8:54 am #

    instead of repeating the error_messages_for method in your errors partial why not simply use that? eg:

    error_messages_for :game 
  2. Darren Terhune
    25. Jul, 2009 at 11:13 am #

    @oli Good call, I did re-write it cause I wanted to style the tags a bit more but I didn’t even think about your way. More Rails like for sure. This worked as well in my controller:

    page.replace_html :ajax_form_errors, "#{error_messages_for :game}"
  3. capitan
    23. Jun, 2010 at 5:09 pm #

    I test it and everything works perfect, my question is how can i do to pass the class and the shake effect to different fields with errors in a form of course with multiple fields, something like an :index,but i don’t know how… thx in advance.

  4. Darren Terhune
    23. Jun, 2010 at 5:50 pm #

    @capitan I’m not quite sure what you mean? Can you be more specific or post some code… then maybe I can help.

  5. capitan
    23. Jun, 2010 at 8:15 pm #

    thanks for your quick response. I’ll try to explain better…
    i have a form with multiple fields for example: name, address, etc.
    many of this

    Nombre:

    how do i pass a different id for each of this?, so in the controller i can use to applied the :shake effect and the .addClassName for the correct field, something like:

    id=’team_name_para’

    the other part of the error, the error_messages_for :object works perfect!

  6. Darren Terhune
    23. Jun, 2010 at 8:27 pm #

    @capitan You could use githubs gists or other pastie type web app to post your code or just use ‘code’ tags to wrap your comment.

  7. capitan
    23. Jun, 2010 at 8:35 pm #

    many of this:

    Name:

    how do i do something like this?

    id=’team_name_para’

    the problem is, i can render the error_messages_for, but when i have many fields, i don’t know how applied the addClassName to the correct field, so the user can see where the error is.

  8. Darren Terhune
    23. Jun, 2010 at 8:51 pm #

    @captain Ok I think I know what your trying to do… so in your controller in the render :update do |page| block you could do something like this:

    team.errors.each_error do |attr,err|
       page.visual_effect :shake, "team_#{attr}_para"
       page < < "$('team_#{attr}_para').addClassName('some_error_class');"
    end
    

    This would loop over what ever objects have errors on them and apply the visual effect, however I'm not sure how parallel effects will work like this... usually you would use Effect.Parallel from scriptaculous to combine multiple effects at the same time.

  9. capitan
    23. Jun, 2010 at 10:13 pm #

    Yes, that’s exactly what i looking for, just one more question, how do i removeClassName(‘some_class’) when some field with error is corrected? , works really good!

  10. Darren Terhune
    23. Jun, 2010 at 11:24 pm #

    @capitan I would almost be inclined to use javascript fully in your case and not use rails at all. But if you like this should work just fine. Place this before your first javascript insertion:

    page < < "$('myform').getInputs().invoke('removeClassName', 'some_error_class');"

    That should remove the error class from all elements of your form, then if the user has entered in valid data it won't appear to have errors.

  11. capitan
    24. Jun, 2010 at 10:43 am #

    Work perfect!!! thanks a lot, just to know where are you from? i’m from argentina!!

  12. Darren Terhune
    24. Jun, 2010 at 10:56 am #

    @capitan I’m from Vancouver Island, British Columbia Canada… F#&K I love Argentina! Went there for 2 months last year. Wish I had a house there. ;)

  13. Komodo
    25. Jun, 2010 at 12:43 pm #

    Hey man, you saved me a lot of time.

    I had your same problem looking into google.

  14. Darren Terhune
    25. Jun, 2010 at 12:54 pm #

    @Komodo glad it helped you out ;)