3

I'm currently playing with AngularJS. I'd like to return, from a service, a variable that will let the scope know when it has changed.

To illustrate this, have a look at the example from www.angularjs.org, "Wire up a backend". Roughly, we can see the following:

var projects = $firebase(new Firebase("http://projects.firebase.io"));
$scope.projects = projects;

After this, all updates made to the projects object (through updates, be it locally or remotely) will be automatically reflected on the view that the scope is bound to.

How can I achieve the same in my project? In my case, I want to return a "self-updating" variable from a service.

var inbox = inboxService.inboxForUser("fred");
$scope.inbox = inbox;

What mechanisms let the $scope know that it should update?

EDIT: In response to the suggestions, I tried a basic example. My controller:

$scope.auto = {
    value: 0
};

setInterval(function () {
    $scope.auto.value += 1;
    console.log($scope.auto.value);
}, 1000);

And, somewhere in my view:

<span>{{auto.value}}</span>

Still, it only displays 0. What am I doing wrong ?

aspyct
  • 3,625
  • 7
  • 36
  • 61

2 Answers2

10

UPDATE:

I made a demo plunker: http://plnkr.co/edit/dmu5ucEztpfFwsletrYW?p=preview
I use $timeout to fake updates.


The trick is to use plain javascript references:

  • You need to pass an object to the scope.
  • You mustn't override that object, just update or extend it.
  • If you do override it, you lose the "binding".
  • If you use $http it will trigger a digest for you.
  • So, whenever a change occurs, the scope variable reference to same object that gets updated in the service, and all the watchers will be notified with a digest.
  • AFAIK, That's how $firebase & Restangular work.
  • If you do multiple updates you need to have a way of resetting properties.
  • Since you hold a reference to an object across the application, you need to be aware of memory leaks.

For example:

Service:

app.factory('inboxService', function($http){

  return {
    inboxForUser: function(user){

      var inbox = {};

      $http.get('/api/user/' + user).then(function(response){
        angular.extend(inbox, response.data);
      })

      return inbox; 
    }
  };
});

Controller:

app.controller('ctrl', function(inboxService){
  $scope.inbox = inboxService.inboxForUser("fred");
});
Ilan Frumer
  • 32,059
  • 8
  • 70
  • 84
  • Thanks for this. I tried to apply your solution (update the object) as illustrated in my edited question, but couldn't get it to work. Any idea? – aspyct Feb 04 '14 at 22:10
  • @Antoine_935 can you share a plunker so I'll take a look? – Ilan Frumer Feb 04 '14 at 22:17
  • Hmmm. Making this plunker made me realise that I should probably use $timeout instead for this example. Alright then, it should work :) http://plnkr.co/edit/OWjMT9hWCn4ujjCnc7eQ?p=preview Thanks for showing me plunker btw, didn't know it! – aspyct Feb 04 '14 at 22:23
  • But then, should I call `$digest` myself? (as maybe no one will in my case). How should I call it, if I don't have a scope at hand? Not sure I want to call it on the `$rootScope`, it may be heavy. – aspyct Feb 04 '14 at 22:26
  • 1
    Even if you trigger $apply on any scope it would always trigger a digest on the $rootScope anyway. That's a know issue and the angular team is currently working on a solution. – Ilan Frumer Feb 04 '14 at 22:32
  • Oh right, I can use $timeout to wrap my update. Doesn't sound too clean though. Is there a better solution ? – aspyct Feb 04 '14 at 22:32
  • $timeout by default triggers another digest cycle , see my update. – Ilan Frumer Feb 04 '14 at 22:40
4

It depends on how the object is updating. If it gets updated "within" angular, a digest cycle will be triggered (See http://docs.angularjs.org/guide/scope), and the view will update automatically. That is the beauty of Angular.

If the object gets updated "outside" of angular (e.g. a jQuery plugin), then you can manually trigger a digest cycle by wrapping the code that's doing the updating in an $apply function. Something like this:

$scope.$apply(function() {
    //my non angular code
});

See http://docs.angularjs.org/api/ng.$rootScope.Scope for more info.

dnc253
  • 39,967
  • 41
  • 141
  • 157