Tengo el siguiente servicio en mi aplicación:
uaInProgressApp.factory('uaProgressService',
function(uaApiInterface, $timeout, $rootScope){
var factory = {};
factory.taskResource = uaApiInterface.taskResource()
factory.taskList = [];
factory.cron = undefined;
factory.updateTaskList = function() {
factory.taskResource.query(function(data){
factory.taskList = data;
$rootScope.$digest
console.log(factory.taskList);
});
factory.cron = $timeout(factory.updateTaskList, 5000);
}
factory.startCron = function () {
factory.cron = $timeout(factory.updateTaskList, 5000);
}
factory.stopCron = function (){
$timeout.cancel(factory.cron);
}
return factory;
});
Luego lo uso en un controlador como este:
uaInProgressApp.controller('ua.InProgressController',
function ($scope, $rootScope, $routeParams, uaContext, uaProgressService) {
uaContext.getSession().then(function(){
uaContext.appName.set('Testing house');
uaContext.subAppName.set('In progress');
uaProgressService.startCron();
$scope.taskList = uaProgressService.taskList;
});
}
);
Así que, básicamente, mi servicio de actualización factory.taskList
cada 5 segundos y he vinculado este factory.taskList
a $scope.taskList
. He probado diferentes métodos como $apply
, $digest
pero los cambios en factory.taskList
no se reflejan en mi controlador y vista $scope.taskList
.
Permanece vacío en mi plantilla. ¿Sabes cómo puedo propagar estos cambios?
Aunque utilizar $watch
puede resolver el problema, no es la solución más eficiente. Es posible que desee cambiar la forma en que está almacenando los datos en el servicio.
El problema es que estás reemplazando la ubicación de memoria a la que está asociada tu taskList
cada vez que le asignas un nuevo valor, mientras que el scope está atascado apuntando a la antigua ubicación. Puedes ver esto sucediendo en this plunk.
Toma una instantánea del heap con Chrome cuando cargues el plunk por primera vez y, después de hacer clic en el botón, verás que la ubicación de memoria a la que apunta el scope nunca se actualiza mientras que la lista apunta a una ubicación de memoria diferente.
Puedes arreglar esto fácilmente haciendo que tu servicio contenga un objeto que contenga la variable que puede cambiar (algo como data:{task:[], x:[], z:[]}
). En este caso "datos" nunca debe ser cambiado, pero cualquiera de sus miembros se puede cambiar siempre que lo necesite. Entonces pasas esta variable data al ámbito y, mientras no la anules intentando asignar "data" a otra cosa, cada vez que cambie un campo dentro de data el ámbito lo sabrá y se actualizará correctamente.
Este plunk muestra el mismo ejemplo ejecutándose usando la solución sugerida arriba. No es necesario usar ningún watcher en esta situación y si alguna vez ocurre que algo no se actualiza en la vista sabes que todo lo que necesitas hacer es ejecutar un scope $apply
para actualizar la vista.
De esta forma se elimina la necesidad de watchers que comparen frecuentemente variables para ver si hay cambios y la fea configuración que implica en los casos en los que se necesita vigilar muchas variables. El único problema con este enfoque es que en tu vista (html) tendrás "data." anteponiendo todo donde solías tener sólo el nombre de la variable.
Angular (a diferencia de Ember y algunos otros frameworks), no proporciona objetos envueltos especiales que semimágicamente se mantienen sincronizados. Los objetos que estás manipulando son objetos javascript planos y al igual que decir var a = b;
no vincula las variables a
y b
, decir $scope.taskList = uaProgressService.taskList
no vincula esos dos valores.
Para este tipo de link
-ing, angular
proporciona $watch
on $scope
. Puedes observar el valor de uaProgressService.taskList
y actualizar el valor de $scope
cuando cambie:
$scope.$watch(function () { return uaProgressService.taskList }, function (newVal, oldVal) {
if (typeof newVal !== 'undefined') {
$scope.taskList = uaProgressService.taskList;
}
});
La primera expresión pasada a la función $watch
se ejecuta en cada bucle $digest
y el segundo argumento es la función que se invoca con el valor nuevo y el antiguo.
No estoy seguro si eso ayuda pero lo que estoy haciendo es vincular la función a $scope.value. Por ejemplo
angular
.module("testApp", [])
.service("myDataService", function(){
this.dataContainer = {
valA : "car",
valB : "bike"
}
})
.controller("testCtrl", [
"$scope",
"myDataService",
function($scope, myDataService){
$scope.data = function(){
return myDataService.dataContainer;
};
}]);
A continuación, sólo se unen en DOM como
<li ng-repeat="(key,value) in data() "></li>
De esta manera usted puede evitar el uso de $watch en su código.