AngularJS How can I watch $ viewValue instead of ngModel?

I am using debounce on the input:

<input type="text"
     ng-model="model.qty"
     ng-model-options="{ debounce : 1000 }"
     min="{{model.min}}" max="{{model.max}}" step="1"
     qty-input validate-model-setting>

      

I have a directive that handles disabling the increase and decrease button for this input:

app.directive('qtyInput', function() {
    return {
        restrict: 'A',
        require: '?ngModel',
        link: function(scope, element, attrs, ngModelCtrl) {
            scope.$watch(attrs.ngModel, function(n, o) {
                var val = parseInt(n);
                if(!isNaN(val)) {
                    if(val + 1 > model.max) {
                        scope.quantityIncDisabled = true;
                    } else {
                        scope.quantityIncDisabled = false;
                    }
                    if(val - 1 < model.min) {
                        scope.quantityDecDisabled = true;
                    } else {
                        scope.quantityDecDisabled = false;
                    }
                }
            });
        }
    }
});

      

The problem is that the watch looks at the model according to this directive. I need to look at instead $viewValue

. This is because there is a race condition between the input to input and the use of the increase / decrease buttons due to the deviation. For example, you can keep pressing the decrement button again and again for exactly 1 second after the input reaches 1 (min), and only after that, after the rollback is complete, the decrement button will become disabled. Instead, I want the button to be disabled immediately when the input reaches 1, rather than after waiting a full second of debugging. My best guess is what it means to put $watch

in $viewValue

, but I don't know how to get to it in the directive.

For reference: Buttons themselves avoid race conditions by changing the value of the input and then activating the input, which makes the debugging work smoothly both when typing input and when using buttons.

   link: function(scope, element, attrs) {
        element.bind('click', function() {
            $timeout(function() {
                var input = element.parent().find('input');
                var changingTo = parseInt(input[0].value) + parseInt(scope.inc);
                if(scope.inc < 0 || isNaN(changingTo)) {
                    var min = parseInt(input[0].min);
                    if(isNaN(changingTo)) {
                        changingTo = min;
                    } else if(changingTo < min) {
                        return;
                    }
                } else {
                    var max = parseInt(input[0].max);
                    if(changingTo > max) {
                        return;
                    }
                }
                input[0].value = String(changingTo);
                input.trigger('input');
            });
        });
    }

      

+5


source to share


4 answers


The docs have an answer: https://code.angularjs.org/1.2.19/docs/api/ng/type/ngModel.NgModelController

You want to use $viewChangeListeners

instead $watch

like this (you may need to click on $formatters

instead depending on how debounce works):



ngModelCtrl.$viewChangeListeners.push(function(){
  var val = parseInt(ngModelCtrl.$viewValue);
  if(!isNaN(val)) {
    ...
  }
});

      

Although I haven't played around with the debounce parameter yet, so I don't know if it will help solve your problem, but this is how you look at viewValue.

+4


source


This will watch $ viewValue ngModelController if the tag has ng-model and match attribute. You can also look at other ngModelController $ viewValues, for example, in a form with many input directives.



    (function () {
        'use strict';

        angular
            .module('app.commons')
            .directive('match', MatchValidator);

        function MatchValidator() {
            return {
                require: 'ngModel',
                link: LinkFunction
            };

            function LinkFunction(scope, element, attrs, ngModel) {
                scope.$watch(function(){
                        return ngModel.$viewValue;
                    }, 
                    function(newValue, oldValue){
                        // do something
                    }
                );
            }
        }
    })();

      

+2


source


An alternative solution to view $ viewValue is to allow invalid ng-model updates and then you can view the model.

ng-model-options="{allowInvalid: true}"

      

see: https://docs.angularjs.org/api/ng/directive/ngModelOptions

This is not a direct answer to this question, but I think it will help in many cases when people visit this thread.

+2


source


Personally, I had to use $ formatters to avoid waiting for debugging.

0


source







All Articles