【问题标题】:AngularJS : $watch vs. $observe when newValue === oldValueAngularJS:当 newValue === oldValue 时,$watch 与 $observe
【发布时间】:2015-08-02 02:55:03
【问题描述】:

如果这已经在某处解释或讨论过,我很抱歉,但我找不到任何地方讨论过这个确切的问题。

所以我有一个带有一个数据绑定“myvar”的角度指令(= 或 @ 没有区别)。数据绑定的值在指令中使用:scope.myvarStr = scope.myvar + 'somestring'。然后我在模板中绑定 myvarStr。

因为 scope.myvarStr 必须在 scope.myvar 更改时进行修改,所以我使用 $watch('myvar', function(...)) 来观察值并在需要时更新 scope.myVarStr。在 watch 函数中我把经典的 if (newValue === oldValue) return;

问题从 $watch 第一次触发时就开始了,两个值相等;然后视图没有更新。我可以很容易地从链接函数第一行的 console.log(scope.myvar) 中看到 scope.myvar 是未定义的(或“依赖于绑定类型”),并且该值已更改为其他值我在 $watch 中做了一个 console.log。

我用谷歌搜索了一个小时左右,发现了这个:https://github.com/angular/angular.js/issues/11565 但是,这个问题在其他任何地方都没有讨论过,所以我在 Google 上搜索了一下,发现了 $observe AngularJS : Difference between the $observe and $watch methods

当我从 $watch 更改为 $observe 时,我的所有问题都消失了,我仍然可以使用 if(newValue === oldValue) return;。

(function(directives) {
'use strict';

directives.directive('someDir', [function() {
    return {
        restrict: 'E',
        scope: {
            myvar: '=' //or @ didn't matter at the time...
        },
        template: '<p>{{myvarStr}}</p>',
        link: function(scope, el, attrs) {

            function toString() {
                if (scope.myvar < 1000) {
                    scope.myvarStr = scope.myvar;
                } else {
                    scope.myvarStr = scope.myvar/1000 + 'k';
                }
            }
            toString();

            scope.$watch('myvar', function(newValue, oldValue) {
                console.log("changed", newValue, oldValue)
                if (newValue == oldValue) return;
                toString();
            },true);

            // When changing to $observe it works :)
            //attrs.$observe('myvar', function(newValue, oldValue) {
            //    console.log("changed", newValue, oldValue)
            //    if (newValue == oldValue) return;
            //    toString();
            //},true);

        }
    };
}]);
}(angular.module('myApp.directives')));

建议:据我了解,$watch 会出现此问题,因为范围值从未更改过。指令需要一段时间才能获取值,直到那时绑定只是一个空字符串或其他东西,当检测到值时 $watch 触发但实际范围值没有改变(或如第一个链接中所述; 当值“出现”在指令中时,第一个手表会触发)。

【问题讨论】:

  • 这对我来说似乎是一个新的变化。我确信手表曾经在运行时为旧值返回 null 或 undefined .... 测试“旧值”是否等于新值对我来说似乎真的很愚蠢。如果旧值未定义,则跳过更有意义。更好的是,有一个 $watch 选项可以不第一次运行。

标签: javascript angularjs angularjs-directive


【解决方案1】:

我不太明白你的建议/解释,但我觉得事情比你说的要简单得多。

您不需要 newValue === oldValue 测试,因为您的 watch-action 是幂等且便宜的。但即使你这样做,也只意味着你需要自己初始化值(例如通过手动调用toString()),你似乎正在这样做,因此你的指令应该按预期工作。 (事实上​​,我无法重现您在代码中提到的问题。)

无论如何,这是一个(简单得多的)working version

.directive('test', function testDirective() {
  return {
    restrict: 'E',
    template: '<p>{{ strVal }}</p>',
    scope: {
      val: '='
    },
    link: function testPostLink(scope) {
      scope.$watch('val', function prettify(newVal) {
        scope.strVal = (!newVal || (newVal < 1000)) ?
            newVal : 
            (newVal / 1000) + 'K';
      });
    }
  };
})

顺便说一句,由于您只是尝试“格式化”某些值以进行显示,因此 filter 似乎比 $watch 更合适(并且清除 imo)(参见 @987654322 @):

.filter('prettyNum', prettyNumFilter)
.directive('test', testDirective)

function prettyNumFilter() {
  return function prettyNum(input) {
    return (!input || (input < 1000)) ? input : (input / 1000) + 'K';
  };
}

function testDirective() {
  return {
    template: '<p>{{ val | prettyNum }}</p>',
    scope: {val: '='}
  };
}

【讨论】:

  • 嗯,问题实际上是关于一个使用 $watch 和 if(newValue === oldValue) return; 的事实。不能保证 toString() 函数会被调用。如果未使用正确的值调用 toString,则不会更新视图。顺便说一句,过滤器非常聪明。以前没有用过太多,但它们在这里可能会派上用场!
  • 我还是不明白这个问题。如果你在newValue === oldValue 时不调用toString() 并且值永远不会改变,那么为什么从未调用过toString() 呢?
  • 这并不意外。问题是当 DOM 渲染时,scope.myvar 是指令内的一个空字符串。当 $watch 第一次触发时,这两个值相等(比如说 1400 === 1400)。所以值永远不会更新
  • @stianlp:我无法重现您提到的问题。可以发一个复制品吗?
猜你喜欢
  • 1970-01-01
  • 2018-07-27
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 2019-02-10
  • 2013-01-30
  • 1970-01-01
相关资源
最近更新 更多