AngularJS 监听对象数组中的对象属性变化

53

我在控制器中有这个数据

    $scope.data = {
        home: {
            baseValue: "1",
            name: "home"
        },
        contact: {
            baseValue: "2",
            name: "contract"
        }
     // a lot more options
    };

像这样带有一些HTML:

<section class="content row" ng-repeat="item in data">
   {{item.name}}
   ....
</section>
现在,我想知道何时更改了 baseValue,但由于我在数据变量中使用了对象,所以无法以更简单的方式watch属性。我尝试过类似这样的东西,但我不得不循环整个数组。
$scope.$watch('data', function (newValue, oldValue, scope) {
 // some code to compare the tow arrays, line by line
}, true);

我应该如何简单地使用$watch,仅在更改baseValue时得到通知?

类似问题:

更新1

我可以为每个对象添加一个单独的监视器以了解何时更改baseValue,但如果我有n个对象,而不仅仅是像这个示例中那样只有几个对象,这将不太好。

$scope.$watch('data.home', function (newValue, oldValue, scope) {
 // do some stuff with newvalue.baseValue
}, true);

$scope.$watch('data.contact', function (newValue, oldValue, scope) {
 // do some stuff with newvalue.baseValue
}, true);
... // Adds more individual `watch`

你只想知道 baseValue 何时发生了变化,但又不想为它添加单独的监视器吗? - lucuma
关于您的更新1,您能具体说明一下“不好”的含义吗? - Jerska
使用属性描述符怎么样?如果不行,可以退而求其次使用*Timeout吗? - public override
你需要知道旧值吗?还是只需要观察变化? - hutingung
或者 $watchGroup?这是 $watch() 的一种变体,它监视一个 watchExpressions 数组。如果集合中的任何一个表达式发生更改,则执行监听器。 - hutingung
4个回答

35
根据您的问题,您可以使用 ngChange 监控 baseValue 的变化并触发该函数。
HTML
<section class="content row" ng-repeat="item in data">
    Name: {{item.name}} <br/>
    BaseValue: <input type="text" ng-model="item.baseValue" ng-change="baseValueChange(item.baseValue)"/>
</section>

控制器

$scope.baseValueChange = function(baseValue) {
    console.log("base value change", baseValue);
}

如果你需要一个更复杂的版本,可以获取旧值和新值,请参考此plunker - http://plnkr.co/edit/hqWRG13gzT9H5hxmOIkO?p=preview

HTML

<section class="content row" ng-repeat="item in data">
    Name: {{item.name}} <br/>
    BaseValue: <input type="text" ng-init="item.oldBaseValue = item.baseValue" ng-model="item.baseValue" ng-change="baseValueChange(item.oldBaseValue, item.baseValue); item.oldBaseValue = item.baseValue"/>
</section>

控制器

$scope.baseValueChange = function(oldVal, newVal) {
    console.log("base value change", oldVal, newVal);
}

这对我有用,但是使用 ng-change 会影响性能,这是真的吗? 这是因为函数不仅在值改变时执行。因此最好使用 watch 来确保只有在数据更新时才执行该函数。 这正确吗? - Coyolero
我认为与观察相比并没有太大的区别。你可能想要比较ng-change和watch之间注册的侦听器数量。我认为是一样的。 - hutingung
我同意+Coyolero的观点。@Jerska提供的解决方案在我的情况下更适合这个问题。 - Elias
我认为这不是一个好的答案,因为它只会在输入控件的更新上触发,而不是从代码中触发。 - csharpsql

16

您可以观察对象属性。
因此,您可以执行类似以下的操作

for(var key in $scope.data) {
  if($scope.data.hasOwnProperty(key)) {
    $scope.$watch("data['" + key + "'].baseValue", function(val, oldVal) {
      // Do stuff
    });
  }
}

没有测试过,但这个想法很简单。


1
我两天前刚开始学习AngularJS,到目前为止我非常喜欢它。这个解决方案正是我所需要的。我注意到ng-change指令只能与与绑定输入交互时更改的值(在我的情况下是复选框)一起使用。我想要在数组元素baseValues发生更改时执行某些操作,无论它们是通过绑定输入还是通过编程方式切换更改的。 - Elias
1
如果项目来来去去,您还应该删除过时的观察者:var w = $scope.$watch(...); 稍后只需调用 w(); 注销。 - z0r
1
$scope.$watch("data[" + key + "].baseValue" .....) 对我没用,但是 $scope.$watch("data['" + key + "'].baseValue" .....) 有效。 - parliament
2
只是为了安全起见,我会使用"data[" + JSON.stringify(key) + "].baseValue"而不是"data['" + key + "'].baseValue" - Hashbrown
1
@Hashbrown 同意。我觉得如果你不知道这个转义字符串的技巧,可能会更加困惑,所以我不会改变答案,但希望你的评论能得到赞同并容易被看到。 - Jerska
显示剩余3条评论

6
在这种情况下,没有绕过使用多个观察器的方法,另一种方法是使用$watchCollection来监视对象值数组,您可以使用Object.values函数获取此数组。
scope.$watchCollection(function() {
    return Object.values(obj);
}, function(newValues, oldValues) {
    // now you are watching all the values for changes!
    // if you want to fire a callback with the object as an argument:
    if (angular.isFunction(scope.callback())) {
        scope.callback()(obj);
    }
});

2
我用以下解决方案解决了这个问题:
$scope.updateFields= function(){
    angular.forEach($scope.fields,function (value, key) {
        value.title = value.title.toLowerCase().replace(/\s+/g,'');
    })
};
$scope.$watch('fields', $scope.updateFields, true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接