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.
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!
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.
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.
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.