How to write a custom validator directive?

Assume that you have a form, in the form you have a text input which is allow you to input an url follow these rule

  • required (can not be empty)
  • accept a-z,A-Z,0-9 and the “_”

OK so how do we implement it?
firstly create a form with an input inside and a button to submit form

<form name="frmSurvey">
    <input type="text" name="article_url"/>
    <button type="button">Submit</button>
</form>

when you create a form like so angularjs automatically create an object with form’s name, let see how is the object look like:

as we see frmSurvey object has some elements such as: $error, $valid… and article_url (the input text),
article_url also is an object has some elements as: $valid, $modelValue, $viewValue $dirty
so now I want when I click Submit button I just need to check like so:

if($scope.frmSurvey.$valid) //do something

Let write a directive to validate for the input

backend.directive('checkValidUrl',checkValidUrl);
function checkValidUrl($timeout,$http){
    return {
        restrict: 'A',
        // require NgModelController, i.e. require a controller of ngModel directive
        require: 'ngModel',
        // create linking function and pass in our NgModelController as a 4th argument
        link: function(scope, element, attr, ctrl) {
            let urlregex = scope.$parent.URL_REGEX;
            function isExist(ngModelValue) {
                let url = '/backend/article/checkURLExist/'+ngModelValue;
                return $http.get(url);
            }
            function doValid(ngModelValue) {
                if (urlregex.test(ngModelValue)) {
                    ctrl.$setValidity('pattern', true);
                } else {
                    ctrl.$setValidity('pattern', false);
                }
                if (ngModelValue.length <= 30) {
                    ctrl.$setValidity('maxLength', true);
                } else {
                    ctrl.$setValidity('maxLength', false);
                }
                let xhr = isExist(ngModelValue);
                xhr.then((res) => {
                    $timeout(function () {
                        let isExist = res.data;
                        if(isExist==="0"){
                            ctrl.$setValidity('unique', true);
                        }
                        else{
                            ctrl.$setValidity('unique', false);
                        }
                    });
                });

                // we need to return our ngModelValue, to be displayed to the user(value of the input)
                return ngModelValue;
            }

            // we need to add our doValid function to an array of other(build-in or custom) functions
            // I have not notice any performance issues, but it would be worth investigating how much
            // effect does this have on the performance of the app
            ctrl.$parsers.push(doValid);
        }
    };
}

 

pay attention on those lines below they are the most important part to add a validator to the form object

(the isExist validator part is just a bonus for unique check, check it out if you need otherwise you can ignore it)
1. set the validaty by ctrl.$setValidity(‘pattern’, true);
2. return ngModelValue; if we dont have this line the $modelValue of input text won have the value, but if you want you still can get the value by $viewValue
3. and finally add the doValid function to an array of other functions, if we don’t have this line nothing will be done

OK so now we have the directive let put it into the input tag

<input type="text" name="article_url" required check-valid-url ng-model="surveyForm.articleUrl"/>

required: Sets required validation error key if the value is not entered.
check-valid-url: our custom validator

let’s see and test by yourself here https://jsbin.com/qisigav/edit?html,js,output

I found using directive is really used for reuse code and makes the code is so concise, beautiful.

thank you for your time.

Add a Comment