Обновить значение области действия при изменении данных службы

У меня есть следующее обслуживание в моем приложении:

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

Затем я использую его в контроллере следующим образом:

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

Итак, в основном мое обновление обслуживания factory.taskList каждые 5 секунд, и я связал это factory.taskList с $scope.taskList. Затем я пробовал различные методы, такие как $apply, $digest, но изменения на factory.taskList не отражаются в моем контроллере и не отображаются $scope.taskList.

Он остается пустым в моем шаблоне. Вы знаете, как я могу распространять эти изменения?

Ответ 1

При использовании $watch может решить проблему, это не самое эффективное решение. Возможно, вы захотите изменить способ хранения данных в службе.

Проблема заключается в том, что вы заменяете ячейку памяти, к которой привязана taskList, каждый раз, когда вы назначаете ей новое значение, когда область застревает, указывая на старое местоположение. Вы можете увидеть это в этой панели.

Сделайте снимки кучи с помощью Chrome при первой загрузке плунжера, и после нажатия кнопки вы увидите, что местоположение памяти, на которое указывает область видимости, никогда не обновляется, а список указывает на другую ячейку памяти.

Вы можете легко исправить это, если ваша служба удерживает объект, который содержит переменную, которая может измениться (что-то вроде data:{task:[], x:[], z:[]}). В этом случае "данные" никогда не должны меняться, но любой из его членов может быть изменен всякий раз, когда вам нужно. Затем вы передаете эту переменную данных в область действия и, пока вы ее не переопределяете, пытаясь назначить "данные" чему-то еще, всякий раз, когда поле внутри данных изменяется, область знаний будет знать об этом и будет корректно обновляться.

В этом листе показан тот же пример, который выполняется с использованием исправления, предложенного выше. Не нужно использовать наблюдателей в этой ситуации, и если когда-нибудь случится, что что-то не обновлено в представлении, то вы знаете, что все, что вам нужно сделать, - это запустить область $apply, чтобы обновить представление.

Таким образом, вы устраняете необходимость в наблюдателях, которые часто сравнивают переменные для изменений и уродливую настройку, участвующих в случаях, когда вам нужно смотреть много переменных. Единственная проблема с этим подходом заключается в том, что на вашем представлении (html) у вас будут "данные" . prefixing все, где вы использовали только имя переменной.

Ответ 2

Angular (в отличие от Ember и некоторых других фреймворков), не предоставляет специальные обернутые объекты, которые полумагически остаются в синхронизации. Объекты, которыми вы управляете, являются явными объектами javascript и так же, как выражение var a = b; не связывает переменные a и b, говоря, что $scope.taskList = uaProgressService.taskList не связывает эти два значения.

Для этого типа link -ing, angular предоставляет $watch в $scope. Вы можете посмотреть значение uaProgressService.taskList и обновить значение на $scope, когда оно изменится:

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

Первое выражение, переданное функции $watch, выполняется в каждом цикле $digest, а второй аргумент - это функция, которая вызывается с помощью нового и старое значение.

Ответ 3

Я не уверен, помогает ли эта помощь, но то, что я делаю, связывает функцию с $scope.value. Например

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

Затем я просто привязываю его в DOM как

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

Таким образом вы можете избежать использования $watch в своем коде.

Ответ 4

Нет $watch или т.д. Вы можете просто определить следующие

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

Поскольку функция getTaskList принадлежит $scope, ее возвращаемое значение будет оцениваться (и обновляться) при каждом изменении uaProgressService.taskList

Ответ 5

Легкая альтернатива заключается в том, что во время инициализации контроллера вы подписываетесь на шаблон уведомления, установленный в службе.

Что-то вроде:

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

И служба имеет что-то вроде:

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

Это позволяет вам более точно настраивать настройки, когда ваши контроллеры обновляются из службы.

Ответ 6

Как сказал Габриэль Пьяченти, часы не нужны, если вы переносите изменяющиеся данные в объект.

НО для правильного обновления измененных служебных данных в области важно, чтобы значение области действия контроллера, использующего служебные данные, не указывало непосредственно на изменение данных (поле). Вместо этого значение области должна указывать на объект, который обертывает изменяющиеся данные.

Следующий код должен объяснить это более ясно. В моем примере я использую службу NLS для перевода. Токены NLS обновляются с помощью http.

Сервис:

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

Вывод контроллера и области видимости

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

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

Вышеприведенный код работает, но сначала я хотел получить доступ к своим токенам NLS напрямую (см. следующий фрагмент), и здесь значения не обновлялись.

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

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