Our web forms are really complex. What's a great solution for extensible form validation, preferably one that works with jQuery?
Background:
Our site has a bit of Ajax, but the real focus is on user experience through about 20 multi-page forms or "wizards." These forms are complicated.
5
into a price field, the field is updated to 5.00
.onSubmit
— we sometimes post multiple forms in order from the same page using Ajax. (E.g., we let users sign up and create a widget in one swoop but, due to legacy systems, that flow requires two POST requests.) We're currently using the jQuery Validation library but our forms appear to be outgrowing its capability. I've been looking at things like <angular/>, Knockout and Backbone.js, but I'm worried that they're too heavyweight or that they would require us to rewrite our frontend.
(This should be a community wiki.)
This is a shameless plug, but might I volunteer a framework that I designed? I've built it based on annotations (a la Hibernate Validator). It has support for custom constraints and I feel that it is pretty powerful. Here is also a Stackoverflow question where I asked for a review of the framework.
5
to 5.00
).Here are a few examples:
The following shows standard validation, with built-in constraints:
<input id = "myInput"
name = "myInput"
type = "text"
class = "regula-validation"
data-constraints = '@NotEmpty @IsNumeric @Between(min=1, max=5)' />
jQuery(document).ready(function() {
// must call regula.binnd() first. The best place would be in an
// onload handler. This function looks for elements with
// a class name of "regula-validation" and binds the
// appropriate constraints to the elements
regula.bind();
jQuery("#myForm").submit(function() {
// this function performs the actual validation
var validationResults = regula.validate();
for(var index in validationResults) {
var validationResult = validationResults[index];
alert(validationResult.message);
}
});
});
As you can see, you're only working with constraint violations, and so the manner in which you display the error message is entirely up to you.
Here's an example of a custom constraint:
regula.custom({
name: "MustBe42",
defaultMessage: "The answer must be equal to 42",
validator: function() {
return this.value == 42;
}
});
And its use:
<input id = "theAnswerToLifeTheUniverseAndEverything"
name = "theAnswerToLifeTheUniverseAndEverything"
value = ""
class = "regula-validation"
data-constraints = "@MustBe42" />
Since the validator is a Javascript function, you can have it do anything (so this addresses your question about side effects).
Here is an example of another constraint that accepts parameters:
regula.custom({
name: "DivisibleBy",
defaultMessage: "{label} must be divisible by {divisor}",
params: ["divisor"],
validator: function(params) {
var divisor = params["divisor"];
return (this.value % divisor) == 0;
}
});
And usage:
<input id = "number"
name = "number"
value = ""
class = "regula-validation"
data-constraints = "@DivisibleBy(divisor=3, label='The Number')" />
Here is an example of using validation groups:
<input id = "score"
name = "score"
type = "text"
class = "regula-validation"
data-constraints = '@IsNumeric(label="Score",
message="{label} needs to be a number!"
groups=[FirstGroup, SecondGroup, ThirdGroup]' />
<input id = "age"
name = "age"
type = "text"
class = "regula-validation"
data-constraints = '@IsNumeric(label="Age",
message="{label} needs to be a number!"
groups=[SecondGroup]' />
<input id = "name"
name = "name"
type = "text"
class = "regula-validation"
data-constraints = '@NotEmpty(label="Name",
message="{label} cannot be empty!"
groups=[FirstGroup]' />
And a snippet that validates only FirstGroup
(so only score
and name
are validated):
var constraintViolations = regula.validate({groups: [regula.Group.FirstGroup]});
var messages = "";
for(var index in constraintViolations) {
var constraintViolation = constraintViolations[index];
messages += constraintViolation.message + "\n";
}
if(messages != "") {
alert(messages);
}
If you're planning on trying it out, I recommend downloading version 1.1.1. The current documentation matches that version specifically. In 1.2.1 I added support for compound constraints, but I haven't updated my documentation to reflect that.
I understand if this doesn't address all your concerns, or if this is not what you are looking for. I thought I'd just put it out there. Also, if you do check it out then I will make sure to update the documentation to reflect version 1.2.1. I've been busy with school and work and so I haven't had the time to do that.
UPDATE #1
Sohnee mentioned client-side validation. I'm actually working on an integration between Regula and Spring 3. Hopefully I should be able to release it sometime soon (depending again, on work and school). The integration works by translating Hibernate validation-constraints to Regula validation-constraints. This way, you only have to write validation code once (mostly). For custom constraints though, you will still have to write code on the Javascript side (the custom validator). But once you annotate code on the server-side with Hibernate validation-constraints, you don't need to do anything on the client-side; those constraints automatically get applied to form elements on the client-side.
Matthew Abbott has also been able to integrate Regula with ASP.NET MVC.
UPDATE #2
I've got a demo webapp (mavenized) on github that showcases the integration between Regula and Spring 3.0.x Web MVC using Hibernate Validator. It's not really documented or anything, it's more proof-of-concept. I plan to add some documentation to the github page about the integration and how it works.
UPDATE #3
I've updated the documentation on the wiki, and it now corresponds to the latest version, 1.2.2 (I made a little bugfix, which is why it is 1.2.2 now).
I have used this jQuery formValidator several times in conjunction with a whole bunch of different environments. I hope it helps as I've rarely spent more than an hour setting it up.
Cheers!
I would say the jQuery Validation plugin does a good job. I have it combined with the metadata plugin to pass the server-side validation parameters to the client. I've also wrapped a few key points to all forms so that I can use a common pattern for validation, and a few exceptional/custom states. This includes a custom alert message and display.
It does not do everything you want out of the box, but it is the best option and the best default behavior I've seen. Again, I do use the metadata (attribute "data-meta") with it. And it can be bent to your will. I'm also using metadata for control binding to input elements client-side. This splits my client-side logic from server-side, but easier in the long run over trying to inject js from the server-side logic.
Parsley.js looks to be a nice and popular choice at time of writing (august 2013).
Answering this myself since someone on our team noticed Validator from jQuery Tools !
pattern
fields make sure the user can only input test in a certain pattern. onFail
and onSuccess
Update: Yep, just reimplemented a chunk of our site with jQuery Tools' validator tooltips. Fantastic!
Server-side validation rocks.
Supply the results of such validation via an AJAX request if you like... or use a server-side framework that will also add client-side validation - but don't write it twice.
Go with jQuery Validation plugins. It never failed me so far
function isEmpty(text) {
if(text == undefined) {
return true;
}
if(text.replace(/\s+/g, ' ').length == 0) {
return true;
}
return false;
}
function isValidBoolean(text) {
if(text == undefined || (text.toLowerCase() != "true" && text.toLowerCase() != "false")) {
return false;
}
return true;
}
function isValidDouble(text) {
var out = parseFloat(text);
if(isNaN(out)) {
return false;
}
return true;
}
function isValidLong(text) {
var out = parseInt(text);
if(isNaN(out)) {
return false;
}
return true;
}
function isValidDate(text) {
if(Date.parseString(text, 'MM/dd/yyyy HH:mm:ss') == null) {
return false;
}
return true;
}
function hasDuplicates(array) {
var valuesSoFar = {};
for (var i = 0; i < array.length; ++i) {
var value = array[i];
if (Object.prototype.hasOwnProperty.call(valuesSoFar, value)) {
return true;
}
valuesSoFar[value] = true;
}
return false;
}
©2020 All rights reserved.