Custom validator for AngularJS applications

It is hard to imagine any application without a form for user input. And an input form without validation of some or all user input fields is also hard to imagine. All web application development frameworks provide some validators out of the box. For example validations like required, min, max, email format etc. are provided by browser or frameworks like jQuery. AngularJS is no different. It also recognizes these standard validations out of box. But domain specific applications always have custom rules and business requirements that require custom validations of user input. In this post, I will show how to write a custom angularJS validator.

For this discussion I will build upon the prototype I showed in one of previous posts, How to use ng-show and ng-hide directives to control visibility of HTML elements. Following snap shot shows the text box on which I am going to use custom directives.

Following code snippet shows the code that has been applied on this text box.


<div class="form-group col-xs-4">
 <label>Quantity</label>
 <input name="qmin" class="form-control" type="text" 
        ng-model="thisListing.Quantity.Minimum" 
        required trade-min="10" trade-is-number 
        placeholder="Number of items" />
 <span class="field-validity-error" ng-show="tradeListingForm.qmin.$error.required">
    Minimum quantity value is required</span>
 <span class="field-validity-error" ng-show="tradeListingForm.qmin.$error.tradeIsNumber">
    Minimum quantity value must be a number</span>
 <span class="field-validity-error" ng-show="tradeListingForm.qmin.$error.tradeMin">
    Minimum quantity must be greater than 10</span>
</div>

The input element has following valdiators applies on it.

  • required: Standard validator that ensures that field is not left empty.
  • trade-min: This is custom validator to ensure that input field value is always greatee than or equal to minimum value set in this attribute.
  • trade-is-number: This is custom validator that ensures that value entered in text box is always a number and not some random text.

Validator for number with [input type=number]

You can argue that why should we need validators to ensure number format and range when these valdiators are already provided when you set input=number on a text box. There are some usability issues and requirements for certain applications. When you set type=number, modern browsers change behavior of the input text box by appending numeric up-down control to text box. But older browsers do not show this up-down control. I am not saying that this is a bad thing that up-down control has been appended. But for lot of applications this control does not provide lot of usability and introduces inconsistent user interface across browsers.

I will not get into usability discussions about this text box type. But for this discussion I am going to use these custom validators because I have some specific requirements for my application for these numeric text boxes.

AngularJS custom validator

Following code snippet shows the custom validators that are being used for these validations.


var INTEGER_REGEXP = /^\-?dc+$/;
var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;

TradePortalApp.directive('tradeIsNumber', function() {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            if (!ctrl) return;
            var validator = function(value) {
                if (ctrl.$isEmpty(value)) {
                    ctrl.$setValidity('tradeIsNumber', false);
                } else {
                    ctrl.$setValidity('tradeIsNumber', NUMBER_REGEXP.test(value));
                }
                return value;
            };
            ctrl.$parsers.unshift(validator);
        }
    };
});
TradePortalApp.directive('tradeMin', function () {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ctrl) {
            if (!ctrl) return;
            var validator = function(value) {
                var validity = true;
                if (attrs.tradeMin) {
                    validity &= parseFloat(value) >= parseFloat(attrs.tradeMin);
                }
                ctrl.$setValidity('tradeMin', validity);
                return value;
            };
            ctrl.$parsers.unshift(validator);
        }
    };
});
TradePortalApp.directive('tradeMax', function () {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            if (!ctrl) return;
            var validator = function (value) {
                var validity = true;
                if (attrs.tradeMax) {
                    validity &= parseFloat(value) <= parseFloat(attrs.tradeMax);
                }
                ctrl.$setValidity('tradeMax', validity);
            };
            ctrl.$parsers.unshift(validator);
        }
    };
});

Following snapshots show effect of these validators when invalid values are entered in that text box.

There are few basic things that I will describe about writing custom validators.

$setValidity

This is the function that is responsible for setting dirty flag on the form and field. When you call this method with second parameter value as false, it adds an entry in $error collection for that control. Later on you can use check $error collection to established validity of field value. You can also use this $error collection to show or hide validation messages.


 <span class="field-validity-error" ng-show="tradeListingForm.qmin.$error.tradeMin">
    Minimum quantity must be greater than 10</span>

The above code shows how validation failure message is shown by checking if tradeMin validity has failed.

Return field value from your validator

If you do not return field value from your validator (parser), undefined value will get set in field value. And if there are other parsers or formatters in the pipeline, they will receive that undefined value. For example in the prototype implementation I always return the field value that was passed in to the parser even if the validation has failed.

Validators that depend on other values in form or model

If you are implementing a complex validators that need to use values from other values in model of your form, then you can find that model in scope object that has been passed to you.

I hope this provides you some starting point for writing custom validators for your applications.

comments powered by Disqus

Blog Tags