Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

set variable in $watch sometimes fails?

Using Angular 1.0.7:

I've a directive which is meant to display a styled checkbox.

See plunkr here.

Sometimes (and not always) my watcher doesnt update one of my scope's variable see:

Here is a failing sequence:

  • foo
  • bar
  • all
  • foo
  • bar

enter image description here

I wonder why the update doesnt occur.

like image 865
apneadiving Avatar asked Oct 03 '22 03:10

apneadiving


1 Answers

It was very interesting question, so problem with combination of your clicked handler and preferences.email watch:

angularjs code:

//https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js line 4510
scope.$watch(function parentValueWatch() {
    var parentValue = parentGet(parentScope);

    if (parentValue !== scope[scopeName]) {
        // we are out of sync and need to copy
        if (parentValue !== lastValue) {
            // parent changed and it has precedence
            lastValue = scope[scopeName] = parentValue;
        } else {
            // if the parent can be assigned then do so
            parentSet(parentScope, parentValue = lastValue = scope[scopeName]);
        }
    }
    return parentValue;
});
break;

You are starting with click on foo + bar and we have:

all: true
foo: true
bar: true

Click by toggle all:

//by this code
var new_val = toggled_input_value();
$scope.model = new_val;

$scope.model is scope[scopeName] from the above code, so scope[scopeName] = false and lastValue = true, parentValue = true (they will be change after $digest run)

$scope.clicked({ value: new_val });

will call

$scope.toggleAll = function(new_value){
  if (new_value){
    $scope.preferences.email.foo = true;
    $scope.preferences.email.bar = true;
  }
  else{
    $scope.preferences.email.foo = false;
    $scope.preferences.email.bar = false;    
  }
}

so,

all: true - $digest have not been run
foo: false
bar: false

and, begin $digest... first call will be:

$scope.$watch('preferences.email', function(new_value){
  var bool = new_value.foo && new_value.bar;
  $scope.preferences.all = bool;
}, true);

so,

all: false
foo: false
bar: false

it's a problem, because on the next parentValueWatch call we'll get:

parentValue = false //$scope.preferences.all
scope[scopeName] = false //$scope.model
lastValue = true

so, parentValue === scope[scopeName], and lastValue have not been updated... it's a bug:)

when you will change $scope.preferences.all to the true, you will get

$scope.preferences.all === lastValue //true

and call

// if the parent can be assigned then do so
parentSet(parentScope, parentValue = lastValue = scope[scopeName]);

so, $scope.preferences.all will become false, instead of a true

you can look at this here http://plnkr.co/edit/YEHqA101YwWKDvK6odFf?p=preview (console.trace)

like image 89
Gm0t Avatar answered Oct 13 '22 11:10

Gm0t