for - ¿Cómo mirar en profundidad una matriz en angularjs?
ng-repeat angularjs (10)
Hay una matriz de objetos en mi alcance, quiero ver todos los valores de cada objeto.
Este es mi código:
function TodoCtrl($scope) {
$scope.columns = [
{ field:''title'', displayName: ''TITLE''},
{ field: ''content'', displayName: ''CONTENT'' }
];
$scope.$watch(''columns'', function(newVal) {
alert(''columns changed'');
});
}
Pero cuando TITLE2
los valores, por ejemplo, cambio TITLE
a TITLE2
, la alert(''columns changed'')
nunca apareció.
¿Cómo observar a fondo los objetos dentro de una matriz?
Hay una demostración en vivo: http://jsfiddle.net/SYx9b/
Aquí hay una comparación de las 3 formas en que puede ver una variable de alcance con ejemplos:
$ watch () es activado por:
$scope.myArray = [];
$scope.myArray = null;
$scope.myArray = someOtherArray;
$ watchCollection () es activado por todo lo que está arriba Y:
$scope.myArray.push({}); // add element
$scope.myArray.splice(0, 1); // remove element
$scope.myArray[0] = {}; // assign index to different value
$ watch (..., true) es activado por TODO lo de arriba Y:
$scope.myArray[0].someProperty = "someValue";
SOLO UNA COSA MÁS...
$ watch () es el único que se activa cuando una matriz se reemplaza por otra matriz, incluso si esa otra matriz tiene el mismo contenido exacto.
Por ejemplo, donde $watch()
se $watchCollection()
y $watchCollection()
no:
$scope.myArray = ["Apples", "Bananas", "Orange" ];
var newArray = [];
newArray.push("Apples");
newArray.push("Bananas");
newArray.push("Orange");
$scope.myArray = newArray;
A continuación se muestra un enlace a un ejemplo de JSFiddle que utiliza todas las diferentes combinaciones de relojes y envía mensajes de registro para indicar qué "relojes" fueron activados
Definir el parámetro objectEquality
(tercer parámetro) de la función $watch
es definitivamente la forma correcta de ver TODAS las propiedades de la matriz.
$scope.$watch(''columns'', function(newVal) {
alert(''columns changed'');
},true); // <- Right here
responde esto suficientemente bien y menciona $watchCollection
también.
Mas detalle
La razón por la que a una pregunta ya contestada es porque quiero señalar que la respuesta de no es buena y no se debe usar.
El problema es que los compendios no suceden inmediatamente. Deben esperar hasta que el bloque de código actual se haya completado antes de ejecutarse. Por lo tanto, ver la length
de una matriz puede pasar por alto algunos cambios importantes que $watchCollection
detectará.
Asume esta configuración:
$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'');
});
A primera vista, puede parecer que estos dispararían al mismo tiempo, como en este caso:
function pushToArray() {
$scope.testArray.push({val:3});
}
pushToArray();
// Console output
// length changed: 2 -> 3
// testArray changed
Eso funciona bastante bien, pero considera esto:
function spliceArray() {
// Starting at index 1, remove 1 item, then push {val: 3}.
$testArray.splice(1, 1, {val: 3});
}
spliceArray();
// Console output
// testArray changed
Tenga en cuenta que la longitud resultante fue la misma, aunque la matriz tiene un elemento nuevo y perdió un elemento, por lo que, en lo que respecta al $watch
, la length
no ha cambiado. $watchCollection
detectó.
function pushPopArray() {
$testArray.push({val: 3});
$testArray.pop();
}
pushPopArray();
// Console output
// testArray change
El mismo resultado ocurre con un push y pop en el mismo bloque.
Conclusión
Para ver cada propiedad en la matriz, use $watch
en la matriz con el tercer parámetro (objectEquality) incluido y establecido en verdadero. Sí, esto es caro pero a veces es necesario.
Para ver cuándo un objeto ingresa / sale de la matriz, use $watchCollection
.
NO use un $watch
en la propiedad length
del array. Casi no hay una buena razón para pensar en hacerlo.
En mi caso, necesitaba ver un servicio, que contiene un objeto de dirección también observado por varios otros controladores. Me quedé atrapado en un bucle hasta que agregué el parámetro ''verdadero'', que parece ser la clave del éxito al observar objetos.
$scope.$watch(function() {
return LocationService.getAddress();
}, function(address) {
//handle address object
}, true);
Esta solución funcionó muy bien para mí, lo hago en una directiva:
alcance. $ watch (attrs.testWatch, function () {.....}, true);
el verdadero funciona bastante bien y reacciona para todos los cambios (agregar, eliminar o modificar un campo).
Aquí hay un plunker de trabajo para jugar con él.
Profundamente viendo una matriz en AngularJS
Espero que esto te sea de utilidad. Si tiene alguna pregunta, no dude en preguntar, intentaré ayudar :)
Hay consecuencias en el rendimiento de sumergir un objeto en tu $ watch. A veces (por ejemplo, cuando los cambios solo son impulsos y saltos), es posible que desee ver un valor calculado fácilmente, como array.length.
Puede establecer el tercer argumento de $watch
en true
:
$scope.$watch(''data'', function (newVal, oldVal) { /*...*/ }, true);
Ver https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch
Desde Angular 1.1.x, también puede usar $ watchCollection para observar la colección (solo el "primer nivel" de).
$scope.$watchCollection(''data'', function (newVal, oldVal) { /*...*/ });
Ver https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection
Si vas a ver solo una matriz, simplemente puedes usar este bit de código:
$scope.$watch(''columns'', function() {
// some value in the array has changed
}, true); // watching properties
Pero esto no funcionará con múltiples arreglos:
$scope.$watch(''columns + ANOTHER_ARRAY'', function() {
// will never be called when things change in columns or ANOTHER_ARRAY
}, true);
Para manejar esta situación, normalmente convierto los múltiples arreglos que quiero ver en JSON:
$scope.$watch(function() {
return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]);
},
function() {
// some value in some array has changed
}
Como @jssebastian señaló en los comentarios, JSON.stringify
puede ser preferible a angular.toJson
ya que puede manejar miembros que comienzan con ''$'' y también otros casos posibles.
Vale la pena señalar que en Angular 1.1.xy superior, ahora puede usar $watchCollection lugar de $ watch. Aunque el $ watchCollection parece crear relojes poco profundos, por lo que no funcionará con matrices de objetos como usted espera. Puede detectar adiciones y eliminaciones de la matriz, pero no las propiedades de los objetos dentro de las matrices.
$ watchCollection logra lo que quieres hacer. A continuación se muestra un ejemplo copiado del sitio web de angularjs http://docs.angularjs.org/api/ng/type/$rootScope.Scope Aunque es conveniente, el rendimiento debe tenerse en cuenta, especialmente cuando se ve una gran colección.
$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);
$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>