Search code examples
javascriptangularjsformsvalidationcustom-validators

Trigger validation on custom validator's scope change


Lets say we have a custom validator with an input as the attribute's value.

app.directive('inputRequired', function() {
    return {
        require: 'ngModel',
        scope: {
            inputRequired: '='
        },
        link: function(scope, elm, attrs, ctrl) {
            ctrl.$validators.inputRequired = function(modelValue) {
                return !(scope.inputRequired && ctrl.$isEmpty(modelValue));
            };
        }
    };
});

On the scope we define a variable and a function to toggle the variable's value:

$scope.isRequired = false;

$scope.toggle = function() {
    $scope.isRequired = !$scope.isRequired;
};

Then we create a form where we are going to use the custom validator. We add a button to call the toggle function, too.

<form>
    <input type="text" ng-model="someModel" input-required="isRequired"/>
    <button ng-click="toggle()">toggle</button>
</form>

How should this work? When the form is loaded and the scope is initialized, the value of isRequired is set to false. So the input field is not required. When we click on the toggle button, value of isRequiredis changed to true. But! Validation is not triggered, although a variable on the validator's scope was changed.

Important note: this is only an example. I know the ng-required directive, which implements this functionality. I need a general solution for the cases when a validator has an input and validity of the field depends on that input. If the input is changed, the field must be revalidated immediately.


Solution

  • just found out a solution: add a watcher on the scope in the link function and call $validate when inputRequired change:

    app.directive('inputRequired', function() {
        return {
            require: 'ngModel',
            scope: {
                inputRequired: '='
            },
            link: function(scope, elm, attrs, ctrl) {
                ctrl.$validators.inputRequired = function(modelValue) {
                    return !(scope.inputRequired && ctrl.$isEmpty(modelValue));
                };
    
                scope.$watch("inputRequired", function() {
                    ctrl.$validate(); 
                });
            }
        };
    });