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 ?

2 Answers 2

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");
});
Sign up to request clarification or add additional context in comments.

7 Comments

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?
@Antoine_935 can you share a plunker so I'll take a look?
Hmmm. Making this plunker made me realise that I should probably use $timeout instead for this example. Alright then, it should work :) plnkr.co/edit/OWjMT9hWCn4ujjCnc7eQ?p=preview Thanks for showing me plunker btw, didn't know it!
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.
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.
|
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.