1

I'm trying to update a view through a controller that sources its data from a service. For some reason, the view doesn't update when the service's data changes. I've distilled an example from my application here. I've tried all sorts of bindings ($scope.time = TimerService.value, wrapped in a function, using $watch - no success).

Note, in my original app, this is an array of objects and an object's attribute changes.

-- script.js --

var mod = angular.module('mymodule', []);

mod.service('TimerService', function() {
  this.value = 0;
  var self = this;
  setInterval(function() {
    self.value += 1;
  }, 2000)
});

mod.controller('TimerCtrl', ['TimerService', '$scope', function(TimerService, $scope) {
  $scope.time = TimerService.value;
  $scope.$watch(function() {
    return TimerService.value;
  }, function(newValue) {
    $scope.time = newValue;  
  }, true);
  $scope.otherValue = '12345';
}]);


angular.element(document).ready(function() {
  alert('start');
  angular.bootstrap(document, ['mymodule']);
});

-- index.html --

<!DOCTYPE html>
<html>

  <head>
    <script src="./angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
      <div ng-controller="TimerCtrl">
          <h1>- {{ time }}-</h1>
          <h2>{{ otherValue }}</h2>
      </div>
  </body>

</html>

3 Answers 3

1

After spending some time with trial and error, I finally found the answer to the problem. In my example above (and my real application), the data gets changed outside of the angular scope (controller button click, http request, etc) which means a digest cycle is not getting kicked off. If I change the code to use $timeout(), it works. Unfortunately, this doesn't entirely solve my problem of the data being changed outside of angular (I guess I need to integrate the rest into angular as well).

Update

I managed to perform changes outside of angular by using rootscope in my service:

$rootscope.$apply(function() {
  // perform variable update here.
  self.value += 1;
});

This successfully propagates to the view.

Sign up to request clarification or add additional context in comments.

3 Comments

When I do this I get a "$digest already in progress" error. Did you run into this same issue?
No, I didn't in my case. But if you do, wrap your call in if(!$rootscope.$$phase). This should help.
Instead of $apply, use $evalAsync
0

Try this

$scope.time = TimerService.value;
if(!$scope.$$phase) {
    $scope.$apply();
}

1 Comment

Nope, doesn't work. I think the problem is that the change isn't being detected.
0

Try

var app = angular.module('myApp', []);
            var value = 0;
            var timer = setInterval('Repeater()', 1000);
            var Repeater = function () {
                value++;
                var scope = angular.element(document.getElementById('myApp')).scope();
                console.log(scope);
                scope.$apply(function () {
                        scope.time = value;
                    });
                };
            app.controller('TimerCtrl', ['$scope', function( $scope) {

          }]);

credit to https://stackoverflow.com/a/16520050/356380

1 Comment

Isn't the 2-way binding the beauty of Angular? Why would I need to deal with DOM elements in a service? In addition, the service may be used everywhere in the application, so I wouldn't know which part of the DOM is affected.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.