简体   繁体   English

$ watch不会在指令中触发

[英]$watch does not fire in directive

In my controller, I set a variable to be true with ng-click . 在我的控制器中,我使用ng-click将变量设置为true。

In my directive, I need to evaluate something when the variable is true. 在我的指令中,当变量为true时,我需要评估某些内容。 Subsequently, I set the variable to be false through my directive. 随后,我通过指令将变量设置为false。

However my problem is that I cannot seem to get the $watch to fire from the ng-click function. 但是我的问题是我似乎无法从ng-click函数中触发$watch

I've included a sample fiddle of my problem. 我提供了一个有关问题的样本。 As you can see, $watch initially logs true in the console on loading the page. 如您所见, $watch最初在加载页面时在控制台中记录为true When I click the directive to make it false , $watch also fires and logs false . 当我单击该指令使其为false$watch也会触发并记录false However subsequent clicks to the button with ng-click do not result in $watch firing. 但是,随后使用ng-click按钮不会导致$watch触发。 Why is this? 为什么是这样?

http://jsfiddle.net/zcouyvwc/2/ 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;
            });

        });
    }
}
}]);

The issue is that someDirective is creating a child scope, and when it sets scope.boolean = false , it actually creates a variable that shadows the original, so the original is unchanged 问题是someDirective正在创建一个子范围,并且当它设置scope.boolean = false ,它实际上创建了一个变量,该变量scope.boolean = false了原始对象,因此原始对象未更改

The standard advice, as from https://github.com/angular/angular.js/wiki/Understanding-Scopes , is 来自https://github.com/angular/angular.js/wiki/Understanding-Scopes的标准建议是

This issue with primitives can be easily avoided by following the "best practice" of always have a '.' 通过遵循始终具有“'”的“最佳实践”,可以很容易地避免使用基元出现此问题。 in your ng-models 在您的ng模型中

If you change your code so that you have 如果您更改代码以便拥有

$scope.model = {
    boolean: true
};

As in http://jsfiddle.net/9j87njvt/1/ , then I think it will behave as expected. 就像在http://jsfiddle.net/9j87njvt/1/中一样 ,那么我认为它的行为将与预期的一样。


As a sidebar, I usually only create directives that depend on scope inheritance in this way if alternatives are more complicated or have some other disadvantage. 作为补充,我通常仅在替代方案更加复杂或存在其他缺点时才以这种方式创建依赖于范围继承的指令。 Usually I pass in options via attributes, and use scope: {...} in the directive definition for an isolated scope, which keeps things more separate. 通常,我通过属性传递选项,并在指令定义中使用scope: {...}作为隔离范围,这使事情更加分离。 Also, I'm not sure why you have $timeout s in a few places. 另外,我不确定为什么在某些地方会有$timeout You might be looking to use scope.$apply() in order for Angular to run the digest cycle to notice changes in the model. 您可能希望使用scope.$apply()来使Angular运行摘要循环来注意到模型中的更改。

You are a victim of prototypical scope inheritance. 您是原型作用域继承的受害者。 You see, specifying scope:true means that the directive creates a new scope that prototypically inherits the parent scope (that of the controller). 您会看到,指定scope:true意味着指令创建了一个新的范围,该范围典型地继承了父范围(控制器的范围)。 (Search for Javascript prototypical inheritance if you are not familiar with the concept.) (如果您不熟悉Javascript原型继承,则进行搜索。)

Take a small break and specify scope:false , meaning the directive uses the parent scope. 稍作休息,然后指定scope:false ,这意味着该指令使用父范围。 Your fiddle works. 你的小提琴奏效。 So, if you have no specific reason to have a child scope, you are done. 因此,如果您没有特定的理由拥有子范围,那么您就完成了。

If not, read on: The characteristic with prototypical scope inheritance is that top level properties (ie $scope.something - in contrast to $scope.something.deepersomething is top level, deeper is not) get read from the first scope in hierarchy that contains them, but get written always to the current scope. 如果没有,请继续阅读:与原型范围继承的特点是顶级属性(即$scope.something -相比于$scope.something.deepersomething是顶级水平, deeper是无法)从第一读取范围包含它们的层次结构,但总是写入当前作用域。 At the beginning, the scope hierarchy is: 最初,作用域层次结构为:

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

After clicking the situation becomes: 单击后,情况变为:

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

The watch is placed on the directive scope, so reads the boolean property from the child scope; 监视放置在指令范围内,因此从子范围读取boolean属性。 in which case it is always false and the watch never triggers. 在这种情况下,它始终是false ,并且手表永远不会触发。

The answer is to use non-top-level properties. 答案是使用非顶级属性。 Place boolean under an object in the controller scope, ie $scope.data.boolean = true and similarily adapt the directive: boolean放在控制器范围内的对象下,即$scope.data.boolean = true并类似地修改指令:

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

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

While you're at it, give boolean a better name! 当您使用它时,请给boolean一个更好的名字! (see answer from developer10) (请参阅developer10的答案)

And use $scope.$apply like above instead of $timeout . 并像上面一样使用$scope.$apply代替$timeout

You should be aware that boolean is a reserved word in many (all) programming languages and as such may not be used for naming purposes. 您应该意识到, boolean是许多(所有)编程语言中的保留字,因此可能不会用于命名目的。

Reserved names 保留名称

First try giving another, meaningful name to your variable and then update us if it still doesn't work. 首先尝试给您的变量赋予另一个有意义的名称,然后在变量仍然无效的情况下更新我们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM