Steven Harlow
Steven Harlow

Reputation: 651

$timeout in service not updating scope variable

I have this simple code here, and I'm not fully understanding why the service is not updating the scope variable. I believe it has something to do with $scope no longer pointing to the memory location of the variable inside the service, but I'm unaware on how to fix.

fooController.js

$scope.count = foo.cycleCount()

index.html

<div>{{count}}</div>

fooService.js

angular.module('app').factory('foo', function($timeout){
  return cycleCount: function(){
  var count = 1;
  $interval(function(){
    if (count === 5){
      count = 1;
    }else{
      count++;
    }
    return count;
  }), 2000);
  return count;
}
});

I'd like a solution that doesn't move the $timout to the controller, and preferably one that doesn't use $watch (though if it must it must).

EDIT WITH OBJECT RETURNED:

fooController.js

$scope.count = foo.cycleCount()

index.html

<div>{{count.num}}</div>

fooService.js

angular.module('app').factory('foo', function($timeout){
  return cycleCount: function(){
  var count = {num: 1 }
  $interval(function(){
    if (count.num === 5){
      count.num = 1;
    }else{
      count.num++;
    }
    return count;
  }), 2000);
  return count;
}
});

Upvotes: 2

Views: 1356

Answers (2)

Enzey
Enzey

Reputation: 5254

You are passing a primitive as opposed to an object. There are two things you can do:

  1. Wrap 'count' in an object and return the object from the service. This way the scope value will also be updated.
  2. Force the scope to pick up the change. This can be done by either using a watch on the service value or setting a timeout to update it.
  3. Have a register and unregister update functions in the service, see snippet below.

_

angular.module('app').factory('foo', function($interval, $parse){
    countUpdaters = {};

    var count = 1;
    $interval(function(){
        if (count === 5){
            count = 1;
        }else{
            count++;
            updateValues(countUpdaters, count);
        }
    }), 2000);

    var updateValues = function(updaters, value) {
        var keys = Object.keys(updaters);
        var values = keys.map(function(v) { return updaters[v]; });
        values.forEach(function(updater) {
            $parse(updater.nameSpace)(updater.scope);
        });
    };

    var services;
    services = {
        registerCountUpdater: function(scope, nameSpace) {
            countUpdaters[scope.$id] = {
                scope: scope, 
                nameSpace: nameSpace
            };
            // Auto-unregister on scope removal
            scope.$on('$destroy', function() {
                services.unregisterCountUpdater(scope);
            });
        },
        unregisterCountUpdater: function(scope) {
            delete countUpdaters[scope.$id];
        }
    };
    return services;

});

Upvotes: 1

Michael
Michael

Reputation: 3326

The $timeout should be:

$timeout(function(){
    $scope.count = foo.cycleCount()
 },0);

The value in your view is binded to the controller, thus that's where the $digest cycle should be force.

Upvotes: 1

Related Questions