Soroush Hakami
Soroush Hakami

Reputation: 5396

Custom validation not triggered when setting model value programmatically

Created a custom validation directive that invalidates an input when anything else than a number or space is input. When I change the value programmatically to something that should pass validation, the validation state is not changed.

Check this JSFIDDLE and see for yourself. Any ideas?

<div ng-app="test" ng-controller="Ctrl">
<form name="form">
    <input custom-validation type="text" ng-model="box.text" name="text" />
</form>
<button ng-click="change()">Change to numbers only</button> 
Why doesn't changing to numbers only pass the validation?
</div>

angular.module('test', []);
angular.module('test').directive('customValidation', function () {
    'use strict';
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            ngModelCtrl.$parsers.push(function removeIllegalCharacters(viewValue) {

                if(viewValue === undefined){
                    ngModelCtrl.$setValidity('numbersAndSpacesOnly', true); //empty value is always valid
                } else {
                    var clean = viewValue.replace(/[^0-9 ]+/g, '');
                    if (viewValue === clean) {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', true);
                    } else {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', false);
                    }
                }

                return viewValue;
            });

        }
    };
});

angular.module('test').controller('Ctrl', function ($scope, $timeout) {
    $scope.change = function () {
        $scope.box.text = '12345';
    }
});

Upvotes: 0

Views: 1113

Answers (1)

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40298

ngModel uses 2 pipelines (arrays) of code for validation:

  1. The $parsers array has functions to apply to the view value when it changes from the user; each function is called with the value returned form the previous, the first function is called with the view value and the return of the last function is written in the model. This is commonly used to validate and convert user input (e.g. from the text of an input type="text" to a number).

  2. The $formatters array works similarly, but in the opposite direction. It receives the model value and transforms it, with the return of the last function being the new view value.

Functions in both pipelines may choose to call ngModel.$setValidity() to alter the validity state of the ngModel.

For the scope of this question: in order to validate the model value, you have to use the $formatters similarly to the $parsers you are already using:

angular.module('test').directive('customValidation', function () {
    'use strict';
    return {
        require: '?ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            function removeIllegalCharacters(value) {
                if(value === undefined){
                    ngModelCtrl.$setValidity('numbersAndSpacesOnly', true); //empty value is always valid
                } else {
                    var clean = value.replace(/[^0-9 ]+/g, '');
                    if (value === clean) {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', true);
                    } else {
                        ngModelCtrl.$setValidity('numbersAndSpacesOnly', false);
                    }
                }

                return value;
            }

            ngModelCtrl.$parsers.push(removeIllegalCharacters);
            ngModelCtrl.$formatters.push(removeIllegalCharacters);
        }
    };
});

Upvotes: 3

Related Questions