Inline validation with Ruby on Rails 3.1 and jQuery

Hi,

I’m going to go over inline form validation (with helpers) similar to how MailChimp does it on their sign up page at: https://mailchimp.com/signup/.  When you click on the form text box a helper slides out from the bottom of the input box. Then after you exit the text box it validates the data that you just put into the field.  It is a super nice way to do helpers on your input forms and validation for your forms.  Our goal is to avoid the standard big red Ruby on Rails error that we’ve come to know and love.

Note that some of the formatting in this blog post was removed.  I think everything is still readable but if not check out the source code. You can get the source code of this tutorial from my GitHub repository: https://github.com/hoitomt/tut_inline_validation

Create the Project

I’m going to start out with a simple rails project and use scaffolding to create a simple form. We will be modifying the html and css so that our validation works the way we want it to.


$ rails new tut_inline_validation

$ rails g scaffold person first_name:string last_name:string

$ rake db:migrate

Next update your Gemfile to include the client_side_validations gem


source 'http://rubygems.org'

gem 'rails', '3.1.3'

gem 'sqlite3'

gem 'client_side_validations'

# Gems used only for assets and not required

# in production environments by default.

group :assets do

  gem 'sass-rails',   '~> 3.1.5'

  gem 'coffee-rails', '~> 3.1.1'

  gem 'uglifier', '>= 1.0.3'

end

gem 'jquery-rails'

group :test do

  # Pretty printed test output

  gem 'turn', '0.8.2', :require => false

end

Then install your bundle


$ bundle install

Configure client_side_validations

The client_side_validations gem requires a bit of setup. The instructions are listed on the GitHub site for the gem and are as follows:

  1. Run the generator for client_side_validations. NOTE: on the github page for client_side_validations it states that an additional generator needs to be run for Rails 3.1+. I did not need that generator (copy_assets) so it isn’t listed.
  2. Update application.js to include the rails.validations path
  3. Update the generated initializer (config/initializers/client_side_validations.rb)
  4. Update your form to use validation

Number 1: Run the generator

$ rails g client_side_validations:install

      create  config/initializers/client_side_validations.rb

*********************

ClientSideValidations

*********************

In your app/assets/javascripts/application.js file add the following:

//= require rails.validations

$

Number 2: Update application.js

After you run the generator there is an instruction at the bottom of the file. We need to update application.js so that it includes the validation code


// This is a manifest file that'll be compiled into including all the files listed below.

// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically

// be included in the compiled file accessible from http://example.com/assets/application.js

// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the

// the compiled file.

//

//= require jquery

//= require jquery_ujs

//= require_tree .

//= require rails.validations

Number 3: Update the initializer


# ClientSideValidations Initializer

require 'client_side_validations/simple_form' if defined?(::SimpleForm)
require 'client_side_validations/formtastic'  if defined?(::Formtastic)
# Uncomment the following block if you want each input field to have the
# validation messages attached.
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
  unless html_tag =~ /^<label/
    %{</pre>
<div class="field_with_errors">#{html_tag}<label class="message" for="#{instance.send(:tag_id)}">#{instance.error_message.first}</label></div>
<pre>}.html_safe
  else
    %{</pre>
<div class="field_with_errors">#{html_tag}</div>
<pre>}.html_safe
  end
end

Number 4 Update the form to use validation.

Open app/views/people/_form.html.erb and update your form_for to add validate => true. I also added an ID to make it easier for jQuery to find the form

</pre>
<%= form_for(@person, :validate => true, :html => {:id => 'awesome-form'}) do |f| %>
<pre>

Now let’s start up the server


$ rails s

If you navigate to http://localhost:3000/people you should see your person/index.html.erb file. So Far so good.

Add Model Validation

Go to person.rb (app/model/person.rb) and add the following validations. Both the first and last name are required. The length of the first name must be between 3 and 30 characters and the last name must be unique.


class Person < ActiveRecord::Base
  validates :first_name, :presence => true,
                         :length => { :within => 3..30 }

  validates :last_name, :presence => true,
                        :uniqueness => { :case_sensitive => false }

end

Let’s make sure they work. From http://localhost:3000/people click on the ‘New Person’ link. Don’t enter anything in the fields and click on “Create Person”.  You should see the following:

Now we know that validations are working. You can see the client_side_validations is doing some nice work already.  It added in the inline validations to the right of the fields and didn’t display the big red box at the top of the form.

jQuery Goodness

Let’s improve the look a bit. Open up app/views/people/_form.html.erb file. Remove the error display and update the html as follows:

 true, :html => {:id => 'awesome-form'}) do |f| %></pre>
<div id="data-entry" class="clearfix">
<div id="label"></div>
<div id="field">
<div id="person_first_name_helper" class="helper">e.g. Barack</div>
<div id="person_first_name_error"></div>
</div>
</div>
<div id="data-entry" class="clearfix">
<div id="label"></div>
<div id="field">
<div id="person_last_name_helper" class="helper">e.g. Obama</div>
<div id="person_last_name_error"></div>
</div>
</div>
<div class="actions"></div>
<pre>

The code above adds containers for the helpers and the error messages so that jQuery has somewhere to put our errors. Now that we have the form html done, let’s add the jQuery. We’ll use Unobtrusive javascript so create a file called sign_up_form.js and put it into app/assets/javascripts

$(function() {
	hideAllHelpers();

	$('#person_first_name').focus(function() {
		$('#person_first_name_helper').show(200);
	});
	$('#person_first_name').blur(function() {
		$('#person_first_name_helper').hide(200);
	});

	$('#person_last_name').focus(function() {
		$('#person_last_name_helper').show(200);
	});
	$('#person_last_name').blur(function() {
		$('#person_last_name_helper').hide(200);
	});

	clientSideValidations.callbacks.element.after = function(element, eventData) {
		// element is the input element (text field). The text field is wrapped by
		// the error so the parent is the error_wrapper. The label is a child of
		// the wrapper and the html of the label is the actual error message
		var elementContainer = element.parents('#field');
		var errorLabel = element.parent().find('label');
		var errorMsg = errorLabel.html();
		errorLabel.hide();
		var existingError = elementContainer.find('#validation-error');
		console.log("Error Msg: " + errorMsg);
		if(!errorMsg || errorMsg == null) {
			existingError.remove();
		} else if(existingError && existingError.length > 0) {
			existingError.html(errorMsg);
		} else {
			elementContainer.append('</pre>
<div id="validation-error">' + errorMsg + '</div>
<pre>
');
		}
	}

});

function hideAllHelpers() {
	$('#person_first_name_helper').hide();
	$('#person_last_name_helper').hide();
}

Here is what the code above is doing:

  • The hideAllHelpers() function is defined at the end of the script.  This hides the helpers upon load
  • The next 8 lines of code show and hide the helpers depending on which text box you are in
  • The clientSideValidation callback is provided by the client_side_validations gem. When Rails validates your field it wraps each text box/label combo in a div. The client_side_validations callback is doing the heavy lifting by making the validation error available to us in javascript.  We work through the DOM to put the error in the correct place.  In the case of this exercise we want the error to end up under the text box.

If you go to http://localhost:3000/person/new you should now see that when you click into a box a helper slides out from underneath.  When you leave the text box it will show a validation error if the text is invalid.

All of the code is on GitHub under https://github.com/hoitomt/tut_inline_validation.  I’ve included a stylesheet out there to help make your form/helpers/validations look awesome.

Thanks for Reading!

References
http://railscasts.com/episodes/263-client-side-validations – It almost goes without saying that one of Ryan Bate’s screen casts would be a reference for a Rails tutorial. He is the king of Rails teaching. Specifically episode 263 is important because it covers the client_side_validations gem

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s