in a directive, $watch fires two times reverting newValue to the original pre $watch firing value

南笙酒味 提交于 2019-12-21 06:31:49

问题


I'm pretty sure I'm doing something wrong because of my little experience with angular. Probably the answer is already somewhere in stackoverflow, and I'm not asking (googling) the right questions.

I have this pretty simple directive. It's just a span with four states, that I want to change on every click on the span. Every click progresses the state forward and then loops back. Every state has a different css class. I want the values changed on the parent model everytime the value of the directive changed (hence the "=" in the isolate scope declaration, right?).

I put a watch on the value to change the class, so that the css class is updated every time the value changes.

Now, the problem is that when I click, the watch is fired with the newValue, but then the watch fires again with newValue equal to the value before the click.

I think this is something that has to do with dirty checking and the connection to model of the parent controller, but then, what is correct angular way of doing this? And/or there is a better, more angulary, way of obtaining the same result?

here the directive :

.directive('square', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope : {
            value : '=',
        },
        link: function(scope, elm, attrs, ctrl) {

            scope.valueToClass = function(value) {
                var result = 'square-empty';
                    if (value == '1') {
                        result = 'square-temp';
                    } else if (value == '2') {
                        result = 'square-confirmed';
                    } else if (value == '3') {
                        result = 'square-critical';
                    }
                return 'block-'+result;
            }

            scope.$watch('value', function(newValue, oldValue) {
                scope.blockClass = scope.valueToClass(newValue)

            }, true);

        },
        controller: function($scope, $element, $attrs) {
            $scope.changeValue = function() {
                $scope.value = ($scope.value+1) % 4;
            }
        },
        template : '<span class="{{blockClass}}"  ng-click="changeValue();">'+'</span>',
        replace: true
    };
});

here >>the fiddle<<


回答1:


It is because you watching for changes in primitive values (integers) of your $scope.block array.

Try this in controller

 $scope.block = [{value: 3}, {value: 2}, {value: 1}, {value: 0}];

and then in view:

<square ng-repeat="squareValue in block" value="squareValue.value"></square>

(Englsh isn't my first language, so sorry for the short answer)




回答2:


You should not be using primitive values for watching changes because due to prototypical inheritance in javascript child scope overrites parent scope values .Kindly see very good article which explains all the reasons. https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance




回答3:


Another answer that might help someone is that you need to check that the newVal has changed, newVal will always be passed in, so you would need check that it is not the same ie:

$scope.$watch('someProperty', function(newVal, oldVal) {
    if (newVal !== oldVal) {
        // do something
    }
});

I found that this solved my issue of $watch listener firing prematurely, regardless of whether the scope property was primitive or not, ie my property was an integer $scope.menuId.



来源:https://stackoverflow.com/questions/15807858/in-a-directive-watch-fires-two-times-reverting-newvalue-to-the-original-pre-w

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!