Search code examples
javascriptangularjsangularjs-directiveangularjs-validation

$watch Listener Not Triggered When ng-maxlength Used


I have a directive that basically watches the value of element.

This element has ng-maxlength attribute which is set to 150. When I pass the 150 characters, the $watch is no longer triggered. Besides, when I remove the whole text with ctrl + x, the $watch is again not triggered.

Input Element

<input type="text" ng-model="company.fullName" name="companyFullName" required ng-maxlength="150" ng-input-validate />

Directive

enlabApp.directive('ngInputValidate', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            scope.$watch(function () { return ctrl.$modelValue; }, function (value) {
                 console.log('test');
            });
        }
    };
});

When I remove the ng-maxlength directive, the problem goes away.


Solution

  • You are using ng-maxlength=150 which mean angular will perform a validation for the number of characters and it will update the $modelValue accordingly. Meaning when it is invalid (if length exceeds) $modelValue will be undefined and then you remove the value by doing ctl-x and then digest cycle runs and again model value becomes undefined since there is no value present and there is no difference from the previous value and the watcher does not trigger (Watcher will trigger only if the modelValue becomes dirty).

    So use $viewValue or element[0].value (which i wont suggest). So do:-

            scope.$watch(function () { 
              return ctrl.$viewValue; 
            }, function (value) {
              console.log('Triggered');
            });
    
    • $viewValue: Actual string value in the view.
    • $modelValue: The value in the model, that the control is bound to.

    Remember that using ng-minlength is only for validation it does not restrict user from entering/pasting more characters, if you want to restrict the minlength attribute is the way to go

    Also note that you do not have to register a watch on ngModel you can use $viewChangeListeners as well instead of creating an additional watch.

    Array of functions to execute whenever the view value has changed. It is called with no arguments, and its return value is ignored. This can be used in place of additional $watches against the model value.

    ctrl.$viewChangeListeners.push(function(){//... });