【问题标题】:How do I ignore the initial load when watching model changes in AngularJS?在 AngularJS 中观看模型更改时如何忽略初始负载?
【发布时间】:2013-06-01 14:47:20
【问题描述】:

我有一个网页作为单个实体的编辑器,它位于 $scope.fieldcontainer 属性中的深度图。从我的 REST API(通过 $resource)获得响应后,我将手表添加到“fieldcontainer”。我正在使用这款手表来检测页面/实体是否“脏”。现在我正在让保存按钮弹跳,但我真的想让保存按钮不可见,直到用户弄脏模型。

我得到的是手表的单个触发器,我认为这是因为 .fieldcontainer = ... 分配在我创建手表后立即发生。我正在考虑只使用“dirtyCount”属性来吸收最初的误报,但这感觉非常hacky......我认为必须有一种“Angular 惯用”的方式来处理这个问题 - 我不是唯一一个使用手表检测脏模型。

这是我设置手表的代码:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

我只是一直在想,除了用“if (dirtyCount >0)”来保护我的“UI弄脏”代码之外,还有一种更清洁的方法来做到这一点......

【问题讨论】:

  • 我还需要能够基于某些按钮重新加载 $scope.fieldcontainer(例如加载实体的先前版本)。为此,我需要暂停手表,重新加载,然后恢复手表。
  • 您会考虑将我的答案更改为接受的答案吗?那些花时间仔细阅读的人往往会同意这是正确的解决方案。
  • @trixtur 我有同样的 OP 问题,但在我的情况下,你的答案不起作用,因为我的旧值不是 undefined。它有一个默认值,在我的模型更新没有提供所有信息的情况下是必需的。所以有些值不会改变,但必须触发。
  • @oliholz 因此,不要检查未定义,而是取出“typeof”并根据您的默认值检查 old_fieldcontainer。
  • @KevinHoffman 您应该将 MW 的答案标记为正确答案,以便其他人找到此问题。这是一个更好的解决方案。谢谢!

标签: angularjs angularjs-scope


【解决方案1】:

第一次调用监听器时,旧值和新值将相同。所以就这样做吧:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

这实际上是 Angular 文档recommend handling it 的方式:

在观察者注册到作用域后,监听器 fn 被异步调用(通过 $evalAsync)来初始化观察者。在极少数情况下,这是不可取的,因为当 watchExpression 的结果没有改变时调用了侦听器。要在侦听器 fn 中检测这种情况,您可以比较 newVal 和 oldVal。如果这两个值相同(===),则由于初始化而调用了侦听器

【讨论】:

  • 这种方式比@rewritten的答案更惯用
  • 这是不正确的。重点是忽略null到初始加载值的变化,没有变化的时候不要忽略变化。
  • 嗯,监听函数总会在手表创建的时候触发。这忽略了第一个电话,这是我相信提问者想要的。如果他特别想在旧值为 null 时忽略更改,他当然可以将 oldValue 与 null 进行比较……但我认为这不是他想要做的。
  • OP 专门询问资源的观察者(来自 REST 服务)。首先创建资源,然后应用观察者,然后写入响应中的属性,然后 Angular 才会循环。使用“正在初始化”标志跳过第一个周期提供了一种仅在已初始化资源上应用观察程序的方法,但仍观察来自/到 null 的更改。
  • 这是错误的,oldValue 在第一次复飞时将为空。
【解决方案2】:

在初始加载之前设置一个标志,

var initializing = true

然后当第一个 $watch 触发时,执行

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

标志将在当前摘要周期结束时被拆除,因此不会阻止下一个更改。

【讨论】:

  • 是的,这可行,但@MW 建议比较新旧价值方法。更简单,更符合 Angular 的习惯。你能更新接受的答案吗?
  • 在第一次火灾时始终将 oldValue 设置为等于 newValue 是专门添加的,以避免必须执行此标志 hack
  • MW 的答案实际上是不正确的,因为旧值的新值无法处理旧值未定义的情况。
  • 对于评论者:这不是一个上帝模型,没有什么能阻止你将“初始化”变量放在装饰器的范围内,然后用它装饰你的“普通”手表。无论如何,这完全超出了这个问题的范围。
  • 请参阅plnkr.co/edit/VrWrHHWwukqnxaEM3J5i 以比较建议的方法,包括在 .get() 回调中设置观察者以及在范围初始化时。
【解决方案3】:

我知道这个问题已经回答了,但是我有一个建议:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

使用标志是可行的,但你不觉得有一点 code smell 吗?

【讨论】:

  • 这是要走的路。在 Coffeescript 中,它就像 return unless oldValue? 一样简单(? 是 CS 中的存在运算符)。
  • 道具上的字码味道!还检查了上帝对象大声笑。太好了。
  • 虽然这不是公认的答案,但这使我的代码在从 API 填充对象并且我想观察不同的保存状态时工作。非常好。
  • 谢谢 - 这对我有帮助
  • 这很好,但是在我的情况下,我在对我的 API 进行 AJAX 调用之前将数据实例化为一个空数组,因此请检查它的长度而不是类型。但这个答案肯定至少显示了最有效的方法。
【解决方案4】:

在初始加载当前值期间,旧值字段未定义。因此,下面的示例可以帮助您排除初始加载。

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

【讨论】:

  • 您可能想使用 !== 因为nullundefined 在这种情况下会匹配,或者('1' == 1 等)
  • 一般来说你是对的。但在这种情况下,由于之前的检查,newValue 和 oldValue 不能是这样的角值。两个值也具有相同的类型,因为属于同一个字段。谢谢。
【解决方案5】:

只验证新val的状态:

$scope.$watch('fieldcontainer',function(newVal) {
      if(angular.isDefined(newVal)){
          //Do something
      }
});

【讨论】:

    猜你喜欢
    • 2013-03-01
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    • 2019-08-08
    • 2015-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多