Table of Contents

Validation

LiveValidation Plugin

The livevalidation plugin reads your model-based validation rules (e.g. “validates_presence_of”) and turns them into client-side javascript-based form validation. Here's the API of the plugin: http://livevalidation.rubyforge.org. By and large, this plugin works okay. There are a few pitfalls though.

validate_format_of for email

Some regular expressions which can be used in Ruby do not translate into JavaScript. Livevalidation literally copies everything out of the validation rules. So:

validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "Please check email format. Example: mike@gmail.com"

Is turned into:

<script type="text/javascript">
  var user_email = new LiveValidation('user_email');user_email.add(Validate.Format, {"validMessage": "", 
                                                                                     "failureMessage": "Please check email format. Example: mike@gmail.com",
                                                                                     "pattern": /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});
  user_email.add(Validate.Length, {"validMessage": "", "minimum": 3, "maximum": 100});
  var user_email_presence = new LiveValidation('user_email',{onlyOnSubmit: true});
  user_email_presence.add(Validate.Presence, {"validMessage": "", "failureMessage": "Please fill in your email address"})
</script>

This lovely piece of javascript code is inserted right underneath your email form field.

The message here is to check your regular epxressions. See to it that they keep working in javascript!

Password Confirmation

The normal usage pattern of “validates_confirmation_of :password” is that you first have a password field, and then a confirmation field. Livevalidation, however, scans your model and add its javascript to the first field. For instance,

validates_confirmation_of :password

results in a situation where the confirmation check is applied immediately when you leave the password field and enter the password-confirmation field. This is very awkward of course, so you'll have to reverse and relabel the fields in your view:

  <p>
    <label for="user_password_confirmation">Password</label><br/>    
    <%= f.password_field :password_confirmation, :onchange => "$('user_password').value=''" %>    
  </p>
  
  <p>
    <label for="user_password">Confirm Password</label><br/>    
    <%= f.password_field :password %>
  </p>

If you take a good look at the ids (as shown in the “for” attribute of the label tag), you'll notice that I've put the confirmation field before the actual password field. This is okay as long as you remember to put your onchange attribute (which clears the second field upon changes in the first field) in the first field as well.

Your Completely Wrong Until Proven Otherwise

Livevalidation's default is to immediately check a form field as you start typing. This is very annoying, as it results in error messages before you've even had a chance to complete the field. So I've made some changes in the javascript file (LiveValidation 1.3, prototype.js version). Take a look at the initialize function (the constructor) of the LiveValidation object. Here you get a chance to change the default values. For example, I've set “onlyOnBlur” to true:

onlyOnBlur: true

Now my form field do not get validated until I leave the field.

Repairing Your Mistake Results in Another Error Message

Another annoying feature is that empty form fields are reported as invalid as well. The following scenario clearly illustrates this:

This is unacceptable, so I've arranged for all validates_presence_of check to be made onSubmit only. Here's my rather ugly hack of the form_helpers.rb file:

      def live_validation(object_name, method)        
        if validations = self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
          field_name = "#{object_name}_#{method}" 
          presence_code = nil          
          validation_types = validations.map do |type, configuration|
            
            if type == :presence
              presence_code = live_validation_code("#{field_name}_presence", type, configuration)
              ''
            else
              live_validation_code(field_name, type, configuration)
            end
           
            ##live_validation_code(field_name, type, configuration)
          end.join(';')
          javascript = initialize_validator(field_name) + validation_types
          if presence_code
            javascript + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code
          else
            javascript
          end          
        else
          ''
        end
      end
      
      def initialize_validator(field_name, options = nil)
        if options
          "var #{field_name}_presence = new LiveValidation('#{field_name}',#{options});"
        else
          "var #{field_name} = new LiveValidation('#{field_name}');"
        end        
      end

Now, for all validates_presence_of validations, a separate LiveValidation object is instantiated which is called only when you submit the form.