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

在AngularJS中观察模型更改时,如何忽略初始负载?,angularjs,angularjs-scope,Angularjs,Angularjs Scope,我有一个网页,用作单个实体的编辑器,该实体在$scope.fieldcontainer属性中作为深度图。从RESTAPI(通过$resource)获得响应后,我向“fieldcontainer”添加了一个手表。我正在使用此手表检测页面/实体是否“脏”。现在我正在使保存按钮反弹,但实际上我想使保存按钮不可见,直到用户弄脏模型 我得到的是手表的一个触发器,我认为这是因为.fieldcontainer=。。。任务在我创建手表后立即发生。我想用一个“dirtyCount”属性来吸收最初的错误警报,但这感

我有一个网页,用作单个实体的编辑器,该实体在$scope.fieldcontainer属性中作为深度图。从RESTAPI(通过$resource)获得响应后,我向“fieldcontainer”添加了一个手表。我正在使用此手表检测页面/实体是否“脏”。现在我正在使保存按钮反弹,但实际上我想使保存按钮不可见,直到用户弄脏模型

我得到的是手表的一个触发器,我认为这是因为.fieldcontainer=。。。任务在我创建手表后立即发生。我想用一个“dirtyCount”属性来吸收最初的错误警报,但这感觉很不舒服。。。我认为必须有一种“角度惯用”的方法来处理这个问题——我不是唯一一个用手表来检测脏模型的人

下面是我设置手表的代码:

 $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脏”代码外,还必须有一种更干净的方法来做到这一点……

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

var initializing = true
当第一个$watch启动时,请执行以下操作:

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

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

第一次调用侦听器时,旧值和新值将相同。那么就这么做吧:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});
这实际上是角度文档的方式:

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


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

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

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

使用标志可以工作,但有一点难度,你不觉得吗?

只是使新val的状态有效:

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

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

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


我还需要能够基于某些按钮重新加载$scope.fieldcontainer(例如加载实体的早期版本)。为此,我需要暂停手表,重新装填,然后重新看表。你能考虑把我的答案改成可接受的答案吗?那些花时间从头到尾阅读的人倾向于认为这是正确的解决方案。@Trixtor我有同样的OP问题,但在我的情况下,你的答案不起作用,因为我的旧值不是未定义的。它有一个默认值,这是在我的模型更新没有提供所有信息的情况下所必需的。因此,有些值不会更改,但必须触发。@oliholz因此,与其检查未定义的值,不如取出“typeof”并对照默认值检查旧的_fieldcontainer。@KevinHoffman您应该将MW的答案标记为正确答案,以便其他人找到此问题。这是一个更好的解决方案。谢谢这种方式比@rewrited的回答更惯用这是不正确的。重点是忽略从
null
到初始加载值的更改,而不是在没有更改时忽略更改。好的,监听器函数总是在创建手表时触发。这忽略了第一次呼叫,我认为这是询问者想要的。如果他特别想在旧值为null时忽略更改,他当然可以将旧值与null进行比较。。。但我不认为这是他想要做的。OP专门询问资源(来自REST服务)的观察者。首先创建资源,然后应用观察者,然后写入响应中的属性,只有这样,Angular才会循环。使用“initializing”(初始化)标志跳过第一个周期提供了一种方法,可以仅在初始化的资源上应用观察程序,但仍在观察从/到
null
的更改。这是错误的,
oldValue
在第一次循环中将为null。这是一种方法。在Coffeescript中,它非常简单,如
return,除非oldValue?
(?是CS中的存在运算符)!还可以查看god object lol。太好了。虽然这不是公认的答案,但这使我的代码在从API填充对象时能够正常工作,我想观察不同的保存状态。非常好。谢谢-这对meThis很有帮助,但是在我的例子中,在对API进行AJAX调用之前,我已经将数据实例化为一个空数组,所以请检查它的长度而不是类型。但这个答案肯定至少表明了最有效的方法。是的,这将起作用,但@MW建议的新旧价值方法的比较。它既简单又有棱角。你能更新被接受的答案吗?总是在第一次开火时将oldValue设置为等于newValue是专门添加的,以避免必须这样做。hackMW的答案实际上是不正确的,因为旧值的新值不能处理旧值未定义的情况。对于评论员来说:这不是上帝模型,没有什么可以阻止您将“initialized”变量放入decorator的范围内,然后用它装饰您的“普通”手表。在任何情况下,这都完全超出了此问题的范围。有关建议的方法的比较,请参阅,包括在.get()回调中设置观察程序和范围初始化。您可能希望使用!==因为
null
undefined
在这种情况下会匹配,或者(
'1'==1
等),所以通常您是对的。但是在这种情况下,由于前面的检查,newValue和oldValue不能是这样的角点值。这两个值的类型相同,因为它们属于同一字段。非常感谢。