Trace: • validation
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:
- You are in an email field where enter your mail address, but you somehow manage to leave out the @ sign
- Then you enter the next field, username, whose presence is required (there's a validates_presence_of in your model)
- Because your email address is invalid you get to see a message on the mail field
- So, you put your cursor back in the mail field to repair things
- But all of a sudden, a new error message shows up, because you've left the username field blank!
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.
You are here: start » ror » validation