Auto-updating scope variables in angularjs
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.
aspyct
Updated on June 04, 2022Comments
-
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 about 10 yearsThanks 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 about 10 years@Antoine_935 can you share a plunker so I'll take a look?
-
aspyct about 10 yearsHmmm. 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 about 10 yearsBut 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 about 10 yearsEven 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 about 10 yearsOh right, I can use $timeout to wrap my update. Doesn't sound too clean though. Is there a better solution ?
-
Ilan Frumer about 10 years$timeout by default triggers another digest cycle , see my update.