如何深入观察angularjs中的数组?
Posted
技术标签:
【中文标题】如何深入观察angularjs中的数组?【英文标题】:How to deep watch an array in angularjs? 【发布时间】:2013-01-20 15:34:22 【问题描述】:我的作用域中有一个对象数组,我想查看每个对象的所有值。
这是我的代码:
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/
【问题讨论】:
【参考方案1】:您可以将$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
【讨论】:
当第三个参数采用boolean value 时,为什么要使用angular.equals
?
感谢 Trevor,误读了文档。我已经更新了上面的代码,并更新了我的代码以匹配。
在 1.1.x 中您现在拥有 $watchCollection
$watchCollection
据我了解,只会观察数组或对象的“第一级”。如果您需要更深入地观察,上述答案是正确的。 bennadel.com/blog/…
很好的答案......如果可以的话,我会给你不止一票...... :) 谢谢【参考方案2】:
在 $watch 中深入挖掘对象会产生性能影响。有时(例如,当更改只是推送和弹出时),您可能想要 $watch 一个容易计算的值,例如 array.length。
【讨论】:
这应该有更多的选票。深度观察是昂贵的。我知道 OP 正在寻找深度观察,但人们可能只是想知道阵列本身是否发生了变化。在这种情况下,观看长度要快得多。 这应该是评论,而不是答案。 如@Blazemonger 评论 (***.com/questions/14712089#comment32440226_14713978) 所述,使用 $watchCollection 可以最大限度地减少性能问题。 简洁的建议。关于此评论不是答案,我认为最好详细说明满足答案的接受标准的建议。我认为通过这种方法可以很好地解决这个问题。【参考方案3】:如果你只看一个数组,你可以简单地使用这段代码:
$scope.$watch('columns', function()
// some value in the array has changed
, true); // watching properties
example
但这不适用于多个数组:
$scope.$watch('columns + ANOTHER_ARRAY', function()
// will never be called when things change in columns or ANOTHER_ARRAY
, true);
example
为了处理这种情况,我通常将我想看的多个数组转换成JSON:
$scope.$watch(function()
return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]);
,
function()
// some value in some array has changed
example
正如@jssebastian 在 cmets 中指出的那样,JSON.stringify
可能比 angular.toJson
更可取,因为它可以处理以“$”开头的成员以及可能的其他情况。
【讨论】:
我还发现$watch
有第三个参数,它可以做同样的事情吗? Pass true as a third argument to watch an object's properties too.
见:cheatography.com/proloser/cheat-sheets/angularjs
@Freewind 如果您需要监视多个数组,它将失败为seen here。但是,是的,这也可以工作,并提供与在单个阵列上使用 angular.toJson
相同的功能。
请注意,angular.toJson 似乎不包含以“$”开头的包含成员:angular.toJson("$hello":"world") 只是“”。我使用 JSON.stringify() 作为替代
@jssebastian 谢谢 - 我已经更新了答案以包含该信息。
我们能知道哪个属性发生了变化吗?【参考方案4】:
值得注意的是,在 Angular 1.1.x 及更高版本中,您现在可以使用 $watchCollection 而不是 $watch。尽管 $watchCollection 似乎创建了浅表,因此它不会像您期望的那样处理对象数组。它可以检测到数组的增删改查,但不能检测数组内对象的属性。
【讨论】:
$watchCollection 只观察浅层,然而。据我了解,更改数组项的属性(如问题所示)不会触发事件。 这应该是评论,而不是答案。【参考方案5】:以下是观察范围变量的 3 种方法的比较:
$watch() 由以下触发:
$scope.myArray = [];
$scope.myArray = null;
$scope.myArray = someOtherArray;
$watchCollection() 由以上所有内容触发 AND:
$scope.myArray.push(); // add element
$scope.myArray.splice(0, 1); // remove element
$scope.myArray[0] = ; // assign index to different value
$watch(..., true) 由以上所有内容触发并且:
$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 的链接,它使用所有不同的监视组合并输出日志消息以指示触发了哪些“监视”:
http://jsfiddle.net/luisperezphd/2zj9k872/
【讨论】:
没错,特别是要注意“更多的事情”【参考方案6】:$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);
【讨论】:
OP 指定了一个对象数组。您的示例适用于字符串数组,但 $watchCollection 不适用于对象数组。【参考方案7】:这个解决方案对我来说效果很好,我在指令中这样做:
scope.$watch(attrs.testWatch, function() ....., true);
true 效果很好,并且可以对所有更改(添加、删除或修改字段)做出反应。
这是一个可以玩的工作小工具。
Deeply Watching an Array in AngularJS
我希望这对你有用。 如果您有任何问题,请随时提出,我会尽力提供帮助:)
【讨论】:
【参考方案8】:在我的例子中,我需要监视一个服务,它包含一个地址对象,也被其他几个控制器监视。在添加 'true' 参数之前,我一直陷入循环,这似乎是观察对象时成功的关键。
$scope.$watch(function()
return LocationService.getAddress();
, function(address)
//handle address object
, true);
【讨论】:
【参考方案9】:设置$watch
函数的objectEquality
参数(第三个参数)绝对是观察数组所有属性的正确方法。
$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
。
不要在数组的length
属性上使用$watch
。我想不出这样做的理由。
【讨论】:
【参考方案10】:$scope.changePass = function(data)
if(data.txtNewConfirmPassword !== data.txtNewPassword)
$scope.confirmStatus = true;
else
$scope.confirmStatus = false;
;
<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>
【讨论】:
以上是关于如何深入观察angularjs中的数组?的主要内容,如果未能解决你的问题,请参考以下文章