Как глубоко наблюдать массив в angularjs?
В моей области есть массив объектов, я хочу посмотреть все значения каждого объекта.
Это мой код:
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);
См. http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch
Так как Angular 1.1.x вы также можете использовать $watchCollection, чтобы смотреть мелкие часы (только "первый уровень" ) коллекции.
$scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ });
См. https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection
Ответ 2
Есть последствия производительности для глубокого погружения объекта в ваши $watch. Иногда (например, когда изменения только толкаются и появляются), вы можете захотеть $смотреть легко рассчитанное значение, например array.length.
Ответ 3
Если вы собираетесь смотреть только один массив, вы можете просто использовать этот бит кода:
$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
, поскольку он может обрабатывать членов, которые начинаются с '$' и других возможных случаев.
Ответ 4
Стоит отметить, что в Angular 1.1.x и выше вы можете теперь использовать $watchCollection, а не $watch. Хотя $watchCollection, похоже, создает мелкие часы, поэтому он не будет работать с массивами объектов, как вы ожидаете. Он может обнаруживать добавления и удаления массива, но не свойства объектов внутри массивов.
Ответ 5
$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 been a change
expect($scope.dataCount).toEqual(3);
Ответ 6
Ниже приведено сравнение трех способов просмотра переменных области с примерами:
$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) запускается ВСЕМ выше 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, который использует все различные комбинации часов и выводит сообщения журнала, чтобы указать, какие "часы" были вызваны:
http://jsfiddle.net/luisperezphd/2zj9k872/
Ответ 7
Это решение работало очень хорошо для меня, я делаю это в директиве:
scope. $watch (attrs.testWatch, function() {.....}, true);
true работает очень хорошо и реагирует на все chnages (добавляет, удаляет или модифицирует поле).
Вот рабочий плункер для игры с ним.
Глубоко просматривая массив в AngularJS
Я надеюсь, что это может быть полезно для вас.
Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать, я постараюсь помочь:)
Ответ 8
В моем случае мне нужно было посмотреть сервис, который содержит адресный объект, также наблюдаемый несколькими другими контроллерами. Я застрял в цикле, пока не добавил параметр "истина", который, кажется, является ключом к успеху при просмотре объектов.
$scope.$watch(function() {
return LocationService.getAddress();
}, function(address) {
//handle address object
}, true);
Ответ 9
Установка параметра 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
для массива. Я почти не думаю, что это можно сделать.
Ответ 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>