如何在angularjs中深度观看数组?

在我的作用域中有一个对象数组,我想观察每个对象的所有值。

这是我的代码:

function TodoCtrl($scope) {
$scope.columns = [
{ field:'title', displayName: 'TITLE'},
{ field: 'content', displayName: 'CONTENT' }
];
$scope.$watch('columns', function(newVal) {
alert('columns changed');
});
}

但是当我修改值时,例如我将TITLE更改为TITLE2alert('columns changed')从未弹出。

如何深度观看数组内的对象?

有一个现场演示:http://jsfiddle.net/SYx9b/

225380 次浏览

如果你只看一个数组,你可以简单地使用这段代码:

$scope.$watch('columns', function() {
// some value in the array has changed
}, true); // watching properties

< a href = " http://jsfiddle.net/4DN5S/ " > < / >示例

但这对多个数组无效:

$scope.$watch('columns + ANOTHER_ARRAY', function() {
// will never be called when things change in columns or ANOTHER_ARRAY
}, true);

< a href = " http://jsfiddle.net/qsMhq/3/ " > < / >示例

为了处理这种情况,我通常会将我想要观察的多个数组转换为JSON:

$scope.$watch(function() {
return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]);
},
function() {
// some value in some array has changed
}

< a href = " http://jsfiddle.net/Egr2T/ " > < / >示例

正如@jssebastian在评论中指出的,JSON.stringify可能比angular.toJson更可取,因为它可以处理以'$'开头的成员,也可以处理其他可能的情况。

你可以将$watch的第3个参数设置为true:

$scope.$watch('data', function (newVal, oldVal) { /*...*/ }, true);

看到https://docs.angularjs.org/api/ng/type/ rootScope.Scope #看美元

从Angular 1.1开始。x你也可以使用$watchCollection来观看浅表(只是“第一层”)的集合。

$scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ });

看到https://docs.angularjs.org/api/ng/type/ rootScope.Scope # watchCollection美元

在$watch中深潜一个对象会对性能造成影响。有时(例如,当更改仅为推和弹出时),您可能希望$watch一个容易计算的值,例如array.length。

值得注意的是,在Angular 1.1中。x及以上,你现在可以使用watchCollection美元而不是$watch。虽然$watchCollection看起来创建的是浅表,所以它不能像你期望的那样处理对象数组。它可以检测数组的添加和删除,但不能检测数组内对象的属性。

这个解决方案对我来说非常有效,我在一个指令中这样做:

范围。看(attrs美元。testWatch, function() {.....},真正的);

true工作得很好,并响应所有更改(添加、删除或修改字段)。

这是一个工作的活塞玩它。

< a href = " http://plnkr.co/edit/evoBgCkMAIkMivE2OoM8?p=preview" rel="nofollow">在AngularJS中深度观察数组 . p=preview" rel="nofollow">

我希望这对你有用。 如果你有任何问题,请尽管问,我会尽力帮助你的:)

在我的例子中,我需要监视一个服务,该服务包含一个地址对象,也由其他几个控制器监视。我陷入了一个循环,直到我添加了“true”参数,这似乎是观察对象时成功的关键。

$scope.$watch(function() {
return LocationService.getAddress();
}, function(address) {
//handle address object
}, true);

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);

设置$watch函数的objectEquality参数(第三个参数)绝对是观察数组所有属性的正确方法。

$scope.$watch('columns', function(newVal) {
alert('columns changed');
},true); // <- Right here

混杀丹很好地回答了这个问题,并提到了$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

同样的结果也发生在同一个块的push和pop操作中。

结论

要观察数组中的每个属性,在数组本身上使用$watch,其中包含第三个参数(objectEquality)并将其设置为true。是的,这很昂贵,但有时是必要的。

要观察对象何时进入/退出数组,使用$watchCollection

不要在数组的length属性上使用$watch。我几乎想不出有什么好的理由这样做。

下面是观察作用域变量的3种方法的比较和示例:

看()美元由以下方式触发:

$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

美元的手表(…,真正的)由上面的所有东西触发:

$scope.myArray[0].someProperty = "someValue";

还有一件事……

看()美元是唯一一个在数组被另一个数组替换时触发的数组,即使另一个数组具有完全相同的内容。

例如,$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/

$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>