Pregunta Actualizar el valor del alcance cuando se cambian los datos del servicio


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 actualización de servicio factory.taskList cada 5 segundos y he vinculado esto factory.taskList a $scope.taskList. Luego probé diferentes métodos como $apply, $digest pero cambia 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?


73
2017-11-02 17:01


origen


Respuestas:


Durante el uso $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á reemplazando la ubicación de memoria que su taskList está asociado a cada vez que le asigna un nuevo valor mientras el alcance está atascado apuntando a la ubicación anterior. Puedes ver que esto sucede en este plunk.

Tome instantáneas de montón con Chrome cuando primero carga el plunk y, después de hacer clic en el botón, verá que la ubicación de la memoria a la que apunta el osciloscopio nunca se actualiza mientras la lista apunta a una ubicación de memoria diferente.

Puede solucionarlo fácilmente haciendo que su servicio contenga un objeto que contenga la variable que puede cambiar (algo así como data:{task:[], x:[], z:[]}) En este caso, los "datos" nunca se deben cambiar, pero cualquiera de sus miembros se puede cambiar cuando lo necesite. A continuación, transfiere esta variable de datos al alcance y, siempre que no la anule intentando asignar "datos" a otra cosa, cada vez que un campo dentro de los datos cambie, el alcance lo sabrá y se actualizará correctamente.

Este plunk muestra el mismo ejemplo que se ejecuta con la solución sugerida anteriormente. No es necesario utilizar ningún observador en esta situación y si alguna vez sucede que algo no se actualiza en la vista, usted sabe que todo lo que necesita hacer es ejecutar un alcance $apply para actualizar la vista

De esta forma, se elimina la necesidad de que los observadores comparen con frecuencia las variables de los cambios y la configuración desagradable que se produce en los casos en los que debe observar muchas variables. El único problema con este enfoque es que en su vista (html) tendrá "datos". prefijando todo donde solía tener el nombre de la variable.


77
2017-10-02 19:41



Angular (a diferencia de Ember y algunos otros marcos), no proporciona especial envuelto objetos que semi-mágicamente mantente sincronizado Los objetos que estás manipulando son simples objetos javascript y simplemente como decir var a = b; no enlazar las variables a y b, diciendo $scope.taskList = uaProgressService.taskList no vincula esos dos valores.

Para este tipo de link-En g, angular proporciona $watch en $scope. Puedes ver el valor de uaProgressService.taskList y actualiza el valor en $scope cuando cambia:

$scope.$watch(function () { return uaProgressService.taskList }, function (newVal, oldVal) {
    if (typeof newVal !== 'undefined') {
        $scope.taskList = uaProgressService.taskList;
    }
});

La primera expresión pasó al $watch la función se ejecuta en cada $digest lazo y el segundo argumento es la función que se invoca con el valor nuevo y el anterior.


71
2017-11-02 17:17



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

Luego solo lo ato en DOM como

<li ng-repeat="(key,value) in data() "></li>

De esta forma puede evitar el uso de $ watch en su código.


42
2017-12-16 09:47



No $watch o etc. es requerido. Simplemente puede definir lo siguiente

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.getTaskList = function() {
      return uaProgressService.taskList;
    };
  });

Porque la función getTaskList pertenece a $scope su valor de retorno será evaluado (y actualizado) en cada cambio de uaProgressService.taskList


5
2018-01-05 14:51



La alternativa ligera es que durante la inicialización del controlador se suscribe a un patrón de notificador configurado en el servicio.

Algo como:

app.controller('YourCtrl'['yourSvc', function(yourSvc){
    yourSvc.awaitUpdate('YourCtrl',function(){
        $scope.someValue = yourSvc.someValue;
    });
}]);

Y el servicio tiene algo así como:

app.service('yourSvc', ['$http',function($http){
    var self = this;
    self.notificationSubscribers={};
    self.awaitUpdate=function(key,callback){
        self.notificationSubscribers[key]=callback;
    };
    self.notifySubscribers=function(){
        angular.forEach(self.notificationSubscribers,
            function(callback,key){
                callback();
            });
    };
    $http.get('someUrl').then(
        function(response){
            self.importantData=response.data;
            self.notifySubscribers();
        }
    );
}]);

Esto puede permitirle ajustar con más cuidado cuando los controladores se actualicen de un servicio.


3
2017-10-09 16:29



Como dijo Gabriel Piacenti, no se necesitan guardias si envuelve los datos cambiantes en un objeto.

PERO para actualizar correctamente los datos de servicio modificados en el alcance, es importante que el valor del alcance del controlador que utiliza los datos de servicio no apunte directamente a los datos cambiantes (campo). En cambio, el valor del alcance debe apuntar al objeto que envuelve los datos cambiantes.

El siguiente código debería explicar esto más claro. En mi ejemplo, uso un servicio NLS para traducir. Los tokens NLS se están actualizando a través de http.

El servicio:

app.factory('nlsService', ['$http', function($http) {
  var data = {
    get: {
      ressources        : "gdc.ressources",
      maintenance       : "gdc.mm.maintenance",
      prewarning        : "gdc.mobMaint.prewarning",
    }
  };
// ... asynchron change the data.get = ajaxResult.data... 
  return data;
}]);

Controlador y expresión de alcance

app.controller('MenuCtrl', function($scope, nlsService)
  {
    $scope.NLS = nlsService;
  }
);

<div ng-controller="MenuCtrl">
  <span class="navPanelLiItemText">{{NLS.get.maintenance}}</span>
</div>

El código anterior funciona, pero primero quería acceder a mis tokens NLS directamente (ver el siguiente fragmento) y aquí los valores no se actualizaron.

app.controller('MenuCtrl', function($scope, nlsService)
  {
    $scope.NLS = nlsService.get;
  }
);

<div ng-controller="MenuCtrl">
  <span class="navPanelLiItemText">{{NLS.maintenance}}</span>
</div>

2
2018-01-23 13:35