Nothing is stopping your users from entering data that doesn’t make
sense to your code. You desperately need quality validation. Today I’d
like to show you a way to achieve it on the client side by evaluating
some of the existing approaches. But first, you might be asking
yourself if the client is 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 can provide a dramatically more fluid user experience, and users expect this more and more as web apps become increasingly dynamic and single page applications become the norm.

What is Quality Validation?.

For our purposes, we will define just a couple key aspects of what makes a high-quality validation tool.

  • I should be able to run multiple validation checks against any given property. For example, a phone number might be too long and have invalid characters. We should see two error messages for this property.
  • I should be able to ask a model if it’s valid, such that the model checks for errors on all of its properties.

With those criteria, let’s take a look at just a couple of our existing options.

What’s Already Out There?

There are indeed plenty of validation techniques available through various front-end libraries, and what’s best for you 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, a two-way data binding framework that uses the concept of observables to map values of JavaScript objects to and from the DOM.

Validation Using Knockout Extenders
Knockout.JS provides a number of handy features, including extenders, which allow you to extend the behavior of your observables. In fact, the example in their documentation suggests a pretty good way to achieve validation already, but based on our criteria, it’s not quite complete. With this implementation, an observable can only have a single 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 build 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.
  • The ability to run multiple validations on an observable.
  • The ability to validate an entire view model.

Validate Using Extenders
For the first goal, I should be able to painlessly declare validation rules using an extender. 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. If I want to accomplish the same with an observable:

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 above, when using the observable, n becomes valid if the value of max is lowered sufficiently. Let’s write our 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 main 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.