How to deep watch an array in angularjs?
在我的作用域中有一个对象数组,我想观察每个对象的所有值。
这是我的代码:
1 2 3 4 5 6 7 8 9 | function TodoCtrl($scope) { $scope.columns = [ { field:'title', displayName: 'TITLE'}, { field: 'content', displayName: 'CONTENT' } ]; $scope.$watch('columns', function(newVal) { alert('columns changed'); }); } |
但是,当我修改这些值时,例如我将
如何深入观察数组中的对象?
现场演示:http://jsfiddle.net/syx9b/
您可以将
1 | $scope.$watch('data', function (newVal, oldVal) { /*...*/ }, true); |
参见https://docs.angularjs.org/api/ng/type/$rootscope.scope$watch
由于Angular 1.1.x,您还可以使用$watchCollection来观看浅表(只是集合的"第一级")。
1 | $scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ }); |
参见https://docs.angularjs.org/api/ng/type/$rootscope.scope$watchcollection
在你的$watch中深潜一个物体会有性能上的影响。有时(例如,当更改只是推送和弹出时),您可能需要$watch一个容易计算的值,例如array.length。
如果只看一个数组,只需使用以下代码:
1 2 3 | $scope.$watch('columns', function() { // some value in the array has changed }, true); // watching properties |
例子
但这不适用于多个数组:
1 2 3 | $scope.$watch('columns + ANOTHER_ARRAY', function() { // will never be called when things change in columns or ANOTHER_ARRAY }, true); |
例子
为了处理这种情况,我通常将要监视的多个数组转换为JSON:
1 2 3 4 5 6 | $scope.$watch(function() { return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]); }, function() { // some value in some array has changed } |
例子
正如@jssebastian在评论中指出的那样,
值得注意的是,在Angular1.1.x及更高版本中,您现在可以使用$watchCollection而不是$watch。虽然$watchCollection似乎创建了浅表,因此它无法像您预期的那样处理对象数组。它可以检测对数组的添加和删除,但不能检测数组中对象的属性。
下面是3种方法的比较,您可以通过示例查看范围变量:
$watch()由以下触发:
1 2 3 | $scope.myArray = []; $scope.myArray = null; $scope.myArray = someOtherArray; |
$watchCollection()由以上所有内容触发,并且:
1 2 3 | $scope.myArray.push({}); // add element $scope.myArray.splice(0, 1); // remove element $scope.myArray[0] = {}; // assign index to different value |
$watch(…,true)由以上所有内容触发,并且:
1 | $scope.myArray[0].someProperty ="someValue"; |
还有一件事…
$watch()是唯一一个在数组替换为另一个数组时触发的数组,即使另一个数组具有相同的精确内容。
例如,如果
1 2 3 4 5 6 7 8 | $scope.myArray = ["Apples","Bananas","Orange" ]; var newArray = []; newArray.push("Apples"); newArray.push("Bananas"); newArray.push("Orange"); $scope.myArray = newArray; |
下面是一个JSfiddle示例的链接,该示例使用所有不同的监视组合并输出日志消息来指示触发了哪些"监视":
http://jsfiddle.net/luisperezphd/2zj9k872/
$watchCollection完成了您想要做的事情。以下是从AngularJS网站http://docs.angularJS.org/api/ng/type/$rootscope.scope复制的示例虽然它很方便,但需要考虑性能,尤其是当您观看大型收藏时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $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); |
在我的例子中,我需要监视一个包含一个地址对象的服务,该对象也由其他几个控制器监视。我陷入了一个循环,直到我添加了"真"参数,这似乎是观察对象成功的关键。
1 2 3 4 5 | $scope.$watch(function() { return LocationService.getAddress(); }, function(address) { //handle address object }, true); |
这个解决方案对我来说非常有效,我在一个指令中这样做:
范围:$watch(attrs.testwatch,function()…..,true);
真的很好地工作,并对所有的技术(添加、删除或修改一个字段)作出反应。
这是一个工作的掠夺者玩它。
深入观察安格拉尔的阵法
我希望这对你有用。如果您有任何问题,请随时咨询,我会尽力帮助您的:)
设置
1 2 3 | $scope.$watch('columns', function(newVal) { alert('columns changed'); },true); // <- Right here |
皮兰很好地回答了这个问题,并提到了
更多细节
我回答一个已经回答的问题的原因是因为我想指出wizardwerdna的答案不好,不应该使用。
问题是消化不会立即发生。它们必须等到当前代码块完成后才能执行。因此,观察数组的
假设此配置:
1 2 3 4 5 6 7 8 9 10 11 12 | $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'); }); |
乍一看,这些可能会同时触发,例如在这种情况下:
1 2 3 4 5 6 7 8 | function pushToArray() { $scope.testArray.push({val:3}); } pushToArray(); // Console output // length changed: 2 -> 3 // testArray changed |
这很有效,但是考虑一下:
1 2 3 4 5 6 7 8 | function spliceArray() { // Starting at index 1, remove 1 item, then push {val: 3}. $testArray.splice(1, 1, {val: 3}); } spliceArray(); // Console output // testArray changed |
注意,即使数组有一个新元素并且丢失了一个元素,结果的长度也是相同的,因此,正如
1 2 3 4 5 6 7 8 | function pushPopArray() { $testArray.push({val: 3}); $testArray.pop(); } pushPopArray(); // Console output // testArray change |
相同的结果发生在同一个块中的推和弹出。
结论
要监视数组中的每个属性,请在包含第三个参数(objectEquality)的数组iteself上使用
要观察对象何时进入/退出阵列,请使用
不要对数组的
1 2 3 4 5 6 7 8 | $scope.changePass = function(data){ if(data.txtNewConfirmPassword !== data.txtNewPassword){ $scope.confirmStatus = true; }else{ $scope.confirmStatus = false; } }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <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> <span style="color:red" ng-show="myForm.checknawPassword.$error.minlength || myForm.checkConfirmPassword.$error.minlength">??????????????????? 5 ????</span> <span ng-show="confirmStatus" style="color:red">?????????????????????</span> <button class="button button-positive button-block" ng-click="saveChangePass(data)" ng-disabled="myForm.$invalid || confirmStatus">???????</button> </form> |