A logic language built specifically to validate web forms, designed to accomplish 3 major goals.
$ npm install uniform-validation --save
Web form validation sucks. This can be shown through a very simple example, consider the following form:
This form is asking the user two major questions:
In order to provide a good user experience, this form should validate itself before sending a request to the server. It also makes sense to disable the three text fields when the user does not have a car. This prevents users from inputting their car's information while accidentially stating that they do not own a car. Such a form would probably have a markup similar to:
<form id="carForm" method="POST" action="/submit">
<input name="hasCar" id="hasCar" type="checkbox" checked="true"/>I have a car
<div id="subForm">
Make: <input name="make" id="make" type="text" /><br />
Model: <input name="model" id="model" type="text" /><br />
Year: <input name="year" id="year" type="text" />
</div>
<button type="submit">Submit</button>
</form>
In order to provide the above functionality, some accompanying JavaScript would be required (assuming jQuery).
// Enable and disable form based on whether or not the user has a car
$(document).on("click", "#hasCar", function (evt) {
if (evt.target.checked) {
$("#subForm :input").attr("disabled", false);
} else {
$("#subForm :input").attr("disabled", true);
}
});
// When the form is submitted, validate it
$(document).on("submit", "#carForm", function (evt) {
if ($("#hasCar").is(":checked")) { // Only require car info if the user owns a car
// Validate that the make and model are not empty
if ($("#make").val() === "" || $("#model").val() === "") {
alert("Please fill in all the required fields.");
evt.preventDefault(); // Don't submit form
return;
}
// Validate that the year is four digits
if (!/^[0-9]{4}$/.test($("#year").val())) {
alert("The year must be exactly four digits.");
evt.preventDefault();
}
}
});
This is far more complicated than it needs to be. The requirements can be stated in a single sentence yet it took over twenty lines of JavaScript. This involved five if-statements, two event listeners with lambda functions, and a great deal of understanding of jQuery. The meaningful logic of the form is hidden behind a bunch of JavaScript and jQuery. Even as an experienced web programmer, I made several rookie mistakes and had to perform multiple Google searches drafting this example.
This is just the frontend too; the server must independently validate all requests to ensure data integrity. This means all the validation code needs to be duplicated and rewritten for a different context! There is also the possibility of bugs, such as a case where the client-side says the form is valid and the server-side says it is not. It is impossible to present a good user-experience in such a case.
To address these issues, the Uniform Validation Language was created. It works as a logical architecture rather than an imperative one to simplify validation logic and make it easy to present a strong user experience. The language can also be run on the server-side as well as the client-side, meaning the source code is shared with no need for duplication on the different environments.
The easiest way to explain how Uniform works is to show an example. Let's take another crack at that form using the same HTML.
// Declare some useful regular expressions
@filled: /./;
@year: /[0-9]{4}/x;
// Declare the form inputs and their types
boolean: hasCar;
string: make;
string: model;
string: year;
// TODO: Should the result tag be used here? I left it out for simplicity.
// We could show its application at the server-side implmentation.
// The entire form is valid if the user does not have a car, or the subform is valid
valid: not hasCar or @mySubForm.valid;
// Define attributes for the subform
@mySubForm {
// The subform is valid if all its inputs are valid
valid: make.valid and model.valid and year.valid;
selector: "#subForm"; // This subForm is the $("#subForm") element
enabled: hasCar; // The subform is enabled only when the user has a car
make {
valid: make matches @filled; // Valid when it is filled
}
model {
valid: model matches @filled; // Valid when it is filled
}
year {
valid: year matches @year; // Valid when it is four digits
}
}
This can be used on the client with only two additional lines of code:
<script src="https://github.com/uniform-team/Uniform-Validation-Language/raw/master/uniform.js"></script> <!-- Include Uniform library -->
<script>uniform.options.href("car.ufm");</script> <!-- Link Uniform code -->
(Assuming a NodeJS server running the Express framework)
Install the Node module: $ npm install uniform-validation --save
var validator = require("uniform-validation");
// TODO: Is the client library necessary to state here? It's completely copy-pasteable and is pretty meaningless to the overall point
// I just don't want to put up an incomplete example and perhaps mislead people.
// Expose the client-side library at /uniform.js
app.get("/uniform.js", function (req, res) {
validator.getClientLib().then(function (lib) {
res.end(lib);
});
});
// On a POST request to /submit, validate against car.ufm
app.post("/submit", validator("car.ufm"), function (req, res) {
res.end("Valid Data!");
}, function (err, req, res, next) {
res.end("Invalid Data!");
});
With only a few lines on the server, all of the Uniform code can be reused to validate against any incoming requests with no customized server logic.
Take a look at the Wiki for additional documentation and resources.
Uniform is still in active development! We would love to hear your feedback about the language, what you like, what you don't like, what features you want, why you use it, or even why you don't. // TODO: How to contact us.
We're doing our best to keep the issues for this project up-to-date and working through them as best we can. Here is a list of some of the major features we're looking forward to implementing.
Did we forget your feature? The best way of getting our attention is to simply file an issue with your brilliant idea.