$ watch中的set变量有时会失败?

时间:2013-12-17 14:32:50

标签: angularjs angularjs-directive

使用Angular 1.0.7:

我有一个用于显示样式复选框的指令。

See plunkr here

有时 (并非总是)我的观察者不会更新我的范围变量之一,请参阅:

这是一个失败的序列:

  • FOO
  • 所有
  • FOO

enter image description here

我想知道为什么更新不会发生。

2 个答案:

答案 0 :(得分:2)

你有一些你不需要的代码。一些代码,不完全确定哪一行,是干扰。主要是,当您在模板中设置ng-model='model'时,您不需要在复选框上手动设置模型,这就是您所需要的!

所以这里是您的代码的工作分叉版本:http://plnkr.co/edit/CzhiAVEAuDtiL2QO95Za并且您可以看到我删除了与模型相关的几乎所有内容,因为它全部是自动的。

让我们总结一下我们做了什么:

  1. 删除了预编译代码,除非你需要在编译之前定义名称?不知道为什么。
  2. 不需要输入值函数,它是对还是错?这是由模型自动完成的。
  3. $ scope.select应该只是切换模型值,其他一切都是自动的。
  4. 删除了init函数,无论如何都会自动设置模型值
  5. 从指令内删除深度手表,您不需要在此处观看DEEP,因为您将最低值传递给它并且不担心其他值。外部切换功能是唯一担心深度观看的事情。
  6. 简化指令代码:

    app.directive("uuCheckbox", function() {
    return {
      restrict: "A",
      transclude: true,
      scope: {
        model: '=',
        clicked: '&'
      },
      template: "<div ng-click='select()'>" + 
        "<span ng-class='{ checked: model }'></span>" + 
        "<input class='hidden' value='true' type='checkbox' ng-model='model'>" + 
        "<span ng-class='{red: model}' ng-transclude></span>" + 
      "</div>",
      compile: function(elmt, tAttrs) {
        return function($scope, element, attrs) {
          element.find('input').attr('name', attrs.name);
          var on_change = function(new_value) {
            $scope.clicked({ value: new_value });
          };
          $scope.$watch('model', on_change);
          $scope.select = function() {
            $scope.model = !$scope.model;
          };
        };
      }
    };
    });
    

答案 1 :(得分:2)

这是一个非常有趣的问题,因此点击处理程序和 preferences.email 组合的问题出现了问题:

angularjs代码:

//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;

您首先点击 foo + bar ,然后点击:

all: true
foo: true
bar: true

点击切换所有

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

$ scope.model来自上面的代码是范围[scopeName] ,因此范围[scopeName] = false和lastValue = true,parentValue = true(它们将在$ digest运行后更改)< / p>

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

将致电

$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;    
  }
}

所以,

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

并且,开始$ digest ...第一个电话将是:

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

所以,

all: false
foo: false
bar: false

这是一个问题,因为在下一个 parentValueWatch 调用中我们会得到:

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

所以, parentValue === scope [scopeName] ,并且lastValue尚未更新......这是一个错误:)

当您将$ scope.preferences.all更改为true时,您将获得

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

并致电

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

所以,$ scope.preferences.all将变为false,而不是真正的

你可以在这里查看http://plnkr.co/edit/YEHqA101YwWKDvK6odFf?p=preview(console.trace)