1 0
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.

Leave a Comment
post icon

Safari renders blank page with Ruby on Rails 2.3 >

If you have upgraded your Rails to version 2.3 you might notice Safari renders a blank page when you access your localhost to view your app. Apparently it’s because the newer versions of Rails don’t work well with older versions of Mongrel. The fix is simple:

sudo gem update mongrel
Leave a Comment
post icon

Prototype and Scriptaculous Drop Down Menu

I’ve made a cool drop down menu system using Prototype and Scriptaculous javascript libraries that utilizes some of the cool effects to create an animated menu system.

Anyway here is droplicious v.2.1 I won’t be putting up new versions here anymore. It will now be at github (see link below… right below)

Droplicious at github: http://github.com/darrenterhune/droplicious

Please comment any bugs or other questions on this post, or submit issues at github Thanks!

Leave a Comment
post icon

Web 2 point 0 website or business name generator

Can’t figure out what to call your sweet new web company? Here’s a cool application that helps you pick out cool web 2.0 company names that you can add to a domain availability checker to see if anyone has beat you to the punch… errrupt! I used it for a recent app I’m developing!

Leave a Comment
post icon

Email to SMS with Rogers – iCal Event Alarm SMS text to Mobile Phone

Here’s how to setup your mobile phone if you don’t have a iPhone to send an SMS text to your phone from iCal Event Alarms using Rogers wireless Canada.

First you need to activate this subscription service with Rogers, it’s free so no worries. So here we go:

Text "subscribe" to 0000000000 (that's 10 ceros FYI).

You should get a text back saying you’ve successfully subscribed to the service! Now you can send emails to your phone and it will get switched from email to SMS using Rogers service. The address to send the emails to is this:

10digitnumber@pcs.rogers.com aka 2507779999@pcs.rogers.com

Now iCal. First go to your address card and click edit. Enter your email number as above into the emails field as “other” and hit edit to save. Set up a regular event just as normal and use the “Email” option as the alarm. Choose your Rogers number email address and choose the date.

Here’s some more mobile phone carriers email or web to sms addresss.

That’s it that’s all.

This post was inspired by the F&$*! that stole my iPhone while I was traveling in South America.

Leave a Comment