Nobody can stop your users from entering data that doesn’t make sense to your code. You will desperately need a quality validation on it. Today I’d like to show you a way to achieve it on the client side by evaluating some of the existing approaches. But before that, there might be few flies flying in your mind and you might be asking to yourself that is the client really where validation should occur?.
Why Do Client Side Validation?
To be clear, back-end validation should always be done, and protections on the database level like unique indexes should also be used where applicable. These are absolutely essential for your data integrity. But performing additional validation on the client side which is must can provide a dramatically more fluid user experience, and users expect this more and more as web apps are become increasingly dynamic and single page applications become the norm now.
What is Quality Validation?.
For our example purpose, we will define just a couple of key aspects of what makes a high-quality validation tool.
- I should be able to run or perform a multiple validation checks against any given property. For example, A mobile phone number might be too long and can have invalid characters. We should be able to see two invalid error messages against it.
- Should be able to ask a client model that does it valid, With that said the model will make a validation check for errors on all of its properties.
With these criteria, let’s take a look at just a couple of our existing validation options.
What’s Already Out There?
There are indeed a plenty of validation techniques available through various front-end libraries, and what’s the best one for you that will depend largely on the existing architecture of your client code. In my case, I recently needed some validation for a front-end that makes ample use of knockout.js, which is a two-way data binding framework does uses the concept of observables which maps the values of JavaScript objects to and from the DOM.
Validation Using Knockout Extenders
Knockout javascript framework provides a number of handy features, including extenders, which allow you to extend the behavior of your observables. In fact, the example in their official documentation suggests a pretty good way to achieve validation, but based on our criteria, it’s not quite complete. With this approach, an observable can only have a single validation error message, and what’s more, we have no way to check the validity of an entire view model – an object composed of Knockout observables that serves as a client side representation of some domain object. However, those things don’t sound too hard to built in, so you can bet that there are packages out there that have already built on this functionality.
Writing my Own Validator
There were three general goals I wanted to achieve for my validation tools:
- The ability to validate an observable against either a literal value or another observable property.
- The ability to run multiple validations on an observable.
- The ability to validate an entire client model.
Validate Using Extenders
For the first target, I should be able to painlessly declare a validation rules using an extender for my observable property. As stated, I should be able to do this with a literal:
var n = ko.observable(4).extend({ gte: { val: 5 } });
console.log(n.errors().length); // 1
The gte validator tells us that n should be greater than or equal to 5. Because n is equal to 4, that observable is in error. Same, If I want to make it work with an observable then:
var max = ko.observable(5);
var n = ko.observable(4).extend({ gte: { val: max } });
console.log(n.errors().length); // 1
max(4);
console.log(n.errors().length); // 0
As you can see in above lines of code, when we are using the observable, n becomes valid, if the value of max is lowered sufficiently. Let’s write our greater than(gte) validation, modeling it after knockout’s required example and adding logic to handle observables.
ko.extenders.gte = function(target, minValue) {
target.hasError = ko.observable(false);
target.validationMessage = ko.observable();
function validate(newValue) {
if (newValue < ko.unwrap(minValue)) {
target.hasError(true);
target.validationMessage("Expected value to be greater than or equal to " + minValue);
}
}
validate(target());
target.subscribe(validate);
if (ko.isObservable(minValue)) {
minValue.subscribe(function(newVal) {
validate(target());
});
}
return target;
};
Notice the two important differences between this and the knockout example:
The validate function checks the value against ko.unwrap(minValue) in case it’s an observable.
After we subscribe to our target observable, we are checking if minValue is observable and subscribing to it if it is.
These two differences allow us to accomplish our first goal. Now, onto the second.
Running Multiple Validations on a Single Observable
One problem with the example above is that if any other validation rule were using the hasError or validationMessage observables, whichever one runs last will simply overwrite any that preceded. We need a mechanism that will add rule specific validation info to our observable. My goal is to be able to rewrite my validation function so that it will access validation information using a key that matches the validation rule, like so:
initializeErrors(target);
function validate(newValue) {
var error = findError(target, 'gte');
if (newValue < ko.unwrap(minValue)) {
addError(target, error, 'gte', "Expected value to be greater than or equal to " + minValue);
} else {
target.errors.remove(error);
}
}
var errorsViewModel = function(key, message) {
this.key = ko.observable(key);
this.message = ko.observable(message);
};
var initializeErrors = function(target) {
target.errors = target.errors || ko.observableArray();
};
var findError = function(target, key) {
return _.find(target.errors(), function(e) {
return e.key() == key;
});
};
var addError = function(target, error, key, message) {
if (!error) {
error = new errorsViewModel(key, message);
target.errors.push(error);
}
};
With these changes, I’ve ensured that defining new validation rules won’t step over any validaiton info that was written by different validation rules. I’ve thus achieved my second goal.
Validate a Whole View Model
For my third goal, I need to validate a view model that’s composed of our extended observables. For this, I’ll define a validateableViewModel function that will extend my view model with some logic that checks for observables with errors. I should be able to use it like this
var ViewModel = function() {
this.n = ko.observable(4).extend({ gte: { val: 5 } });
return ko.validateableViewModel(this);
}
var vm = new ViewModel();
console.log(vm.isValid()); // false
hope you’ll find this useful.