$ watch不会在指令中触发

时间:2014-10-08 06:37:50

标签: javascript angularjs

在我的控制器中,我使用ng-click将变量设置为true。

在我的指令中,我需要在变量为true时评估某些内容。随后,我通过我的指令将变量设置为false。

但是我的问题是我似乎无法从$watch函数触发ng-click

我收录了一个问题的样本。如您所见,$watch最初在加载页面时在控制台中记录true。当我点击指令使其成为false时,$watch也会触发并记录false。但是,对ng-click按钮的后续点击不会导致$watch触发。这是为什么?

http://jsfiddle.net/zcouyvwc/2/

var app = angular.module('myApp', []);

function MyCtrl($scope,$timeout) {

    $scope.boolean = true;

    $scope.setToTrue = function() {
        $timeout(function(){$scope.boolean = true;});
    };
}


app.directive("someDirective",['$timeout',function($timeout) {
return {
    restrict:"A",
    scope:true,
    link: function(scope, element, attrs) {

        scope.$watch("boolean", function() {
         console.log(scope.boolean);
        });

        element.bind('click',function(){
            $timeout(function(){
                scope.boolean = false;
            });

        });
    }
}
}]);

3 个答案:

答案 0 :(得分:3)

问题是someDirective正在创建一个子范围,当它设置scope.boolean = false时,它实际上会创建一个隐藏原始范围的变量,因此原始范围不变

来自https://github.com/angular/angular.js/wiki/Understanding-Scopes的标准建议是

  

通过遵循始终拥有'。'的“最佳实践”,可以轻松避免使用原语这个问题。在您的ng模型中

如果您更改了代码,那么

$scope.model = {
    boolean: true
};

http://jsfiddle.net/9j87njvt/1/一样,我认为它会按预期运作。


作为侧边栏,如果替代方案更复杂或具有其他一些缺点,我通常只会以这种方式创建依赖于范围继承的指令。通常我通过属性传递选项,并在指令定义中使用scope: {...}作为隔离范围,这使事物更加分离。另外,我不确定你为什么在一些地方$timeout。您可能希望使用scope.$apply()来使Angular运行摘要周期以注意模型中的更改。

答案 1 :(得分:1)

您是原型范围继承的受害者。您可以看到,指定scope:true意味着该指令创建了一个新的作用域,该作用域原型继承了父作用域(控制器的作用域)。 (如果您不熟悉这个概念,请搜索Javascript原型继承。)

稍作休息并指定scope:false,这意味着该指令使用父作用域。你的小提琴有效。因此,如果您没有特定的理由拥有子范围,那么您就完成了。

如果没有,请继续阅读:原型范围继承的特征是顶级属性(即$scope.something - 与$scope.something.deepersomething相比是顶级属性{{1}从包含它们的层次结构中的第一个范围读取,但始终写入当前范围。最初,范围层次结构为:

deeper

点击后情况变为:

controller scope (contains "boolean" top level prop, value: true)
|
+- directive child scope (contains nothing)

手表放在指令范围内,因此从子范围读取controller scope (contains "boolean" top level prop, value: true) | +- directive child scope (contains "boolean" top level prop, value: false) 属性;在这种情况下,它总是boolean并且手表永远不会触发。

答案是使用非顶级属性。将false放在控制器范围内的对象下,即boolean,并类似地修改指令:

$scope.data.boolean = true

在你做到这一点的同时,给scope.$watch("data.boolean", function() { ... }); element.bind('click',function(){ scope.$apply(function() { scope.data.boolean = false; ... 一个更好的名字! (见developer10的回答)

并使用上面的boolean代替$scope.$apply

答案 2 :(得分:0)

您应该知道boolean是许多(所有)编程语言中的保留字,因此不能用于命名目的。

Reserved names

首先尝试为您的变量提供另一个有意义的名称,然后如果它仍然不起作用则更新我们。