在观看AngularJS中的模型更改时如何忽略初始负载?


183

我有一个网页可以用作单个实体的编辑器,它位于$ 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(例如,加载实体的先前版本)。为此,我需要先暂停手表,重新加载手表,然后再恢复手表。
凯文·霍夫曼2013年

您是否考虑将我的答案更改为可接受的答案?那些花时间阅读所有内容的人倾向于认为这是正确的解决方案。
trixtur

@trixtur我有同样的OP问题,但是对于我来说,您的答案不起作用,因为我的旧值不是undefined。它有一个默认值,在我的模型更新未提供所有信息的情况下这是必需的。因此,某些值不会改变,而必须触发。
oliholz 2015年

@oliholz因此,不要检查未定义的内容,而要删除“ typeof”并根据您的默认值检查old_fieldcontainer。
trixtur

@KevinHoffman,您应该将MW的答案标记为其他人找到此问题的正确答案。这是一个更好的解决方案。谢谢!
Dev01

Answers:


117

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

var initializing = true

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

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

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


17
是的,这将起作用,但是比较@MW建议的旧值方法和新值方法。既简单又更具Angular惯用语。您可以更新接受的答案吗?
gerryster,2014年

2
总是在第一次发生火灾时始终将oldValue设置为等于newValue,这是专门添加的,以避免必须进行此标记破解
Charlie Martin

2
MW的答案实际上是不正确的,因为将新值替换为旧值将无法处理未定义旧值的情况。
trixtur

1
对于评论者:这不是上帝模型,没有什么可以阻止您将“初始化”变量放入装饰器的范围内,然后用它装饰“常规”手表。无论如何,这完全超出了这个问题的范围。
重写

1
有关建议方法的比较,请参见plnkr.co/edit/VrWrHHWwukqnxaEM3J5i,既可以在.get()回调中设置观察程序,又可以进行范围初始化。
重写

482

第一次调用侦听器时,旧值和新值将相同。因此,只需执行以下操作:

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

这实际上是Angular文档建议处理它的方式

在范围内注册了监视程序后,将异步调用侦听器fn(通过$ evalAsync)以初始化监视程序。在极少数情况下,这是不可取的,因为当watchExpression的结果未更改时会调用侦听器。要在侦听器fn中检测到这种情况,可以比较newVal和oldVal。如果这两个值相同(===),则由于初始化而调用了侦听器


3
这种方式比
@rewrite

25
这是不正确的。要点是忽略从null初始加载值的更改,而不忽略没有更改的更改。
2014年

1
好了,在创建手表时,侦听器功能将始终触发。这忽略了第一个呼叫,这是我认为请求者想要的。如果他特别想在旧值为null时忽略更改,那么他当然可以将oldValue与null进行比较...但是我认为这不是他想要的。
兆瓦。

OP专门询问有关资源监视者(来自REST服务)的信息。首先创建资源,然后应用观察程序,然后写入响应中的属性,然后Angular进行循环。使用“ initializing”标志跳过第一个周期提供了一种仅在初始化的资源上应用观察程序,但仍在监视from / to更改的方法null
重写

7
这是错误的,oldValue在第一次复飞时将为空。
凯文

42

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

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

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

使用标志是可行的,但是有点代码味道,您认为呢?


1
这是要走的路。在Coffeescript中,它很简单return unless oldValue?(?是CS中的存在运算符)。
凯文

1
字码上的道具气味!还检查神对象哈哈。太好了。
马修·哈伍德

1
尽管这不是可接受的答案,但这使我的代码在从API填充对象时可以正常工作,并且我想观察不同的保存状态。非常好。
卢卡斯·雷普·韦兰德

1
谢谢-这对我有帮助
Wand Maker

1
很棒,但是就我而言,在对API进行AJAX调用之前,我已将数据实例化为空数组,因此请检查其长度而不是类型。但是这个答案肯定至少显示出最有效的方法。
Saborknight

4

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

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

你可能想用==因为!null并且undefined将匹配在这种情况下,或('1' == 1等)
杰米·佩特

总的来说你是对的。但是在那种情况下,由于先前的检查,newValue和oldValue不能是这样的角值。这两个值也具有相同的类型,因为它们属于同一字段。谢谢。
塔欣·特科兹

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.