我的作用域中有一个对象数组,我想要观察每个对象的所有值。
这是我的代码:
function TodoCtrl($scope) {
$scope.columns = [
{ field:'title', displayName: 'TITLE'},
{ field: 'content', displayName: 'CONTENT' }
];
$scope.$watch('columns', function(newVal) {
alert('columns changed');
});
}
但是当我修改数值时,例如我将TITLE
更改为TITLE2
,alert('columns changed')
从未弹出。
如何深入观察数组中的对象?
有一个现场演示:http://jsfiddle.net/SYx9b/
答案 0 :(得分:518)
您可以将$watch
的第三个参数设置为true
:
$scope.$watch('data', function (newVal, oldVal) { /*...*/ }, true);
请参阅https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
从Angular 1.1.x开始,您还可以使用$ watchCollection来观看该集合的浅表(只是“第一级”)。
$scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ });
请参阅https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection
答案 1 :(得分:50)
在你的$ watch中深度潜水物体会产生性能影响。有时(例如,当更改只是推送和弹出时),您可能希望$观察一个容易计算的值,例如array.length。
答案 2 :(得分:43)
如果你只想看一个数组,你可以简单地使用这段代码:
$scope.$watch('columns', function() {
// some value in the array has changed
}, true); // watching properties
但是这不适用于多个数组:
$scope.$watch('columns + ANOTHER_ARRAY', function() {
// will never be called when things change in columns or ANOTHER_ARRAY
}, true);
为了处理这种情况,我通常会将我想要观看的多个数组转换为JSON:
$scope.$watch(function() {
return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]);
},
function() {
// some value in some array has changed
}
正如@jssebastian在评论中指出的那样,JSON.stringify
可能优于angular.toJson
,因为它可以处理以'$'开头的成员以及可能的其他案例。
答案 3 :(得分:21)
值得注意的是,在Angular 1.1.x及更高版本中,您现在可以使用$watchCollection而不是$ watch。虽然$ watchCollection似乎创建了浅表,但它不能像你期望的那样使用对象数组。它可以检测数组的添加和删除,但不能检测数组内对象的属性。
答案 4 :(得分:17)
以下是使用示例观看范围变量的3种方法的比较:
$ watch()由以下方式触发:
$scope.myArray = [];
$scope.myArray = null;
$scope.myArray = someOtherArray;
$ watchCollection()由以上所有内容触发:
$scope.myArray.push({}); // add element
$scope.myArray.splice(0, 1); // remove element
$scope.myArray[0] = {}; // assign index to different value
$ watch(...,true)由以上所有内容触发AND:
$scope.myArray[0].someProperty = "someValue";
只是一件事......
$ watch()是用另一个数组替换数组时触发的唯一一个,即使该另一个数组具有相同的确切内容。
例如$watch()
会触发而$watchCollection()
不会:
$scope.myArray = ["Apples", "Bananas", "Orange" ];
var newArray = [];
newArray.push("Apples");
newArray.push("Bananas");
newArray.push("Orange");
$scope.myArray = newArray;
下面是一个示例JSFiddle的链接,它使用所有不同的监视组合并输出日志消息来指示触发了哪些“监视”:
答案 5 :(得分:13)
$ watchCollection 完成您想要做的事情。以下是从angularjs网站http://docs.angularjs.org/api/ng/type/$rootScope.Scope复制的示例 虽然方便,但需要考虑性能,尤其是在观看大型系列时。
$scope.names = ['igor', 'matias', 'misko', 'james'];
$scope.dataCount = 4;
$scope.$watchCollection('names', function(newNames, oldNames) {
$scope.dataCount = newNames.length;
});
expect($scope.dataCount).toEqual(4);
$scope.$digest();
//still at 4 ... no changes
expect($scope.dataCount).toEqual(4);
$scope.names.pop();
$scope.$digest();
//now there's been a change
expect($scope.dataCount).toEqual(3);
答案 6 :(得分:4)
这个解决方案对我很有用,我在指令中这样做:
范围。$ watch(attrs.testWatch,function(){.....},true);
真实的效果非常好并且对所有chnages做出反应(添加,删除或修改字段)。
这是一个有效的玩具。
Deeply Watching an Array in AngularJS
我希望这对你有用。 如果您有任何问题,请随时提问,我会尽力帮助:)
答案 7 :(得分:4)
在我的情况下,我需要观看一个服务,其中包含一个地址对象,该对象也被其他几个控制器监视。我被困在循环中,直到我添加'true'参数,这似乎是观看对象时成功的关键。
$scope.$watch(function() {
return LocationService.getAddress();
}, function(address) {
//handle address object
}, true);
答案 8 :(得分:1)
设置objectEquality
函数的$watch
参数(第三个参数)绝对是观察数组所有属性的正确方法。
$scope.$watch('columns', function(newVal) {
alert('columns changed');
},true); // <- Right here
Piran回答得非常好,并提及$watchCollection
。
更多详情
我回答已经回答的问题的原因是因为我想指出wizardwerdna的答案不是很好,不应该使用。
问题是摘要不会立即发生。他们必须等到当前的代码块完成才能执行。因此,观察数组的length
实际上可能会遗漏$watchCollection
将要捕获的一些重要更改。
假设此配置:
$scope.testArray = [
{val:1},
{val:2}
];
$scope.$watch('testArray.length', function(newLength, oldLength) {
console.log('length changed: ', oldLength, ' -> ', newLength);
});
$scope.$watchCollection('testArray', function(newArray) {
console.log('testArray changed');
});
乍一看,似乎这些会同时发射,例如在这种情况下:
function pushToArray() {
$scope.testArray.push({val:3});
}
pushToArray();
// Console output
// length changed: 2 -> 3
// testArray changed
这很好用,但考虑一下:
function spliceArray() {
// Starting at index 1, remove 1 item, then push {val: 3}.
$testArray.splice(1, 1, {val: 3});
}
spliceArray();
// Console output
// testArray changed
请注意,即使数组有一个新元素并且丢失了一个元素,结果长度也是相同的,因此,就$watch
而言,length
没有被更改。 $watchCollection
虽然接受了它。
function pushPopArray() {
$testArray.push({val: 3});
$testArray.pop();
}
pushPopArray();
// Console output
// testArray change
同一个区块中的推送和弹出也会出现相同的结果。
<强>结论强>
要观察数组中的每个属性,请在数组上使用$watch
并包含第三个参数(objectEquality)并设置为true。是的,这很昂贵但有时是必要的。
要观察对象何时进入/退出数组,请使用$watchCollection
。
请勿在阵列的$watch
属性上使用length
。我几乎没有理由这样做。
答案 9 :(得分:0)
$scope.changePass = function(data){
if(data.txtNewConfirmPassword !== data.txtNewPassword){
$scope.confirmStatus = true;
}else{
$scope.confirmStatus = false;
}
};
&#13;
<form class="list" name="myForm">
<label class="item item-input">
<input type="password" placeholder="ใส่รหัสผ่านปัจจุบัน" ng-model="data.txtCurrentPassword" maxlength="5" required>
</label>
<label class="item item-input">
<input type="password" placeholder="ใส่รหัสผ่านใหม่" ng-model="data.txtNewPassword" maxlength="5" ng-minlength="5" name="checknawPassword" ng-change="changePass(data)" required>
</label>
<label class="item item-input">
<input type="password" placeholder="ใส่รหัสผ่านใหม่ให้ตรงกัน" ng-model="data.txtNewConfirmPassword" maxlength="5" ng-minlength="5" name="checkConfirmPassword" ng-change="changePass(data)" required>
</label>
<div class="spacer" style="width: 300px; height: 5px;"></div>
<span style="color:red" ng-show="myForm.checknawPassword.$error.minlength || myForm.checkConfirmPassword.$error.minlength">รหัสผ่านต้องมีจำนวน 5 หลัก</span><br>
<span ng-show="confirmStatus" style="color:red">รหัสผ่านใหม่ไม่ตรงกัน</span>
<br>
<button class="button button-positive button-block" ng-click="saveChangePass(data)" ng-disabled="myForm.$invalid || confirmStatus">เปลี่ยน</button>
</form>
&#13;