defaultcheckbox defaultcheckbox - 2 months ago 13
AngularJS Question

I have a polling factory using $timeout but I can't update controller after the $timeout runs.

Please view the factory and the controller below. I want to reference the factory in my controller. I want the controller to have access to a running count from the API.

At the moment, I am unable to update the controller with the new value after the $timeout has run.

Ideally I'd like to keep the $timeout in the factory, and not in the controller. I think the problem is that I don't have a callback in the $timeout, can anyone point me in the right direction?

The Factory

angular.module('MyPoller', []).factory('MyPoller', function(
$http,$filter,$timeout) {
var data = { response: {}, calls: 0 };

var poller = function (successCallback) {
$http.get('https://jsonplaceholder.typicode.com/posts').then(function (r) {
data.response = r.data;
data.calls++;
successCallback(data);
$timeout(poller, 10000);
});
};

return {
poller: poller
};
});


The Controller

angular.module('NavBarCtrl', [])
.controller('NavBarCtrl', function NavBarCtrl($scope, $document, $window, $dropdown, $modal, $timeout, MyPoller) {

var ctrl = this;

MyPoller.poller(function(data) {
ctrl.numberNewMessages = data.calls;
});
})

Lex Lex
Answer

You can do this without having to use $scope.$watch() by using a factory with a getter. The issue is that calls is a primitive and so after reading it the first time Angular will no longer automatically check for changes because it is not expecting a primitive to change. If you instead reference the parent object then Angular will watch for changes automatically. Here's a simplified example to illustrate one way that you might do this:

angular.module('app', [])
  .factory('dataService', function($timeout) {
    var _data = {
      response: {},
      calls: 0
    };
    var service = {
      get data() {
        return _data;
      }
    };

    function incrementCalls() {
      _data.calls++;
      $timeout(function() {
        incrementCalls();
      }, 2000);
    }

    incrementCalls();
    return service;
  })
  .controller('ctrl', function($scope, dataService) {
    $scope.data = dataService.data;
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  Calls: {{data.calls}}
</div>

Update - same as above, but without using a getter
The bigger issue is that you want to reference the object returned from the service instead of a primitive property on the object. By referencing the object you get the automatic wiring up by Angular to watch for changes.

angular.module('app', [])
  .factory('dataService', function($timeout) {
    var _data = {
      response: {},
      calls: 0
    };
    var service = {
      data: _data
    };

    function incrementCalls() {
      _data.calls++;
      $timeout(function() {
        incrementCalls();
      }, 2000);
    }

    incrementCalls();
    return service;
  })
  .controller('ctrl', function($scope, dataService) {
    $scope.data = dataService.data;
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
  Calls: {{data.calls}}
</div>