Auto-updating scope variables in angularjs

14,392

Solution 1

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");
});

Solution 2

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.

Share:
14,392
aspyct
Author by

aspyct

Updated on June 04, 2022

Comments

  • aspyct
    aspyct almost 2 years

    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
    aspyct about 10 years
    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?
  • Ilan Frumer
    Ilan Frumer about 10 years
    @Antoine_935 can you share a plunker so I'll take a look?
  • aspyct
    aspyct about 10 years
    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!
  • aspyct
    aspyct about 10 years
    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.
  • Ilan Frumer
    Ilan Frumer about 10 years
    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.
  • aspyct
    aspyct about 10 years
    Oh right, I can use $timeout to wrap my update. Doesn't sound too clean though. Is there a better solution ?
  • Ilan Frumer
    Ilan Frumer about 10 years
    $timeout by default triggers another digest cycle , see my update.