I'm trying the following code. Everything seems to be working as I see the component appear/disappear depending on the value I set for the variable. However, when I do that in the setTimeout(...)
function, it starts to misbehave. The poofy text shows but the value set to vm
doesn't. My guess is that I need to pass it in somehow but I'm not sure how.
(function () {
'use strict';
angular
.module("app")
.controller("Banana", banana);
function banana() {
var vm = this;
vm.showStuff = false;
setTimeout(function () {
console.log("poof!");
vm.showStuff = true;
}, 1000);
}
})();
Do I need to make the view-model globally accessible?
Try use the script bellow.
(function () {
'use strict';
angular
.module("app")
.controller("Banana", banana);
function banana($timeout) {
var vm = this;
vm.showStuff = false;
$timeout(function () {
console.log("poof!");
vm.showStuff = true;
}, 1000);
}
})();
To be noted - there's additional step required.
- Substitute
setTimeout(...)
for $timeout(...)
.
- Pass
$timeout
into banana(...)
.
- Provide
banana.$inject = ["$timeout",...]
.
Use the $apply method with setTimeout:
//WORKS
setTimeout(function () {
$scope.$apply("vm.showStuff = true");
}, 1000);
OR use the AngularJS $timeout service:
//RECOMMENDED
$timeout(function () {
vm.showStuff = true;
}, 1000);
The window.setTimeout method creates an event outside the AngularJS framework and its digest cycle. The $timeout service wraps window.setTimeout
and integrates it with the AngularJS framework and its digest cycle.
Angular modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and Angular execution context. Only operations which are applied in Angular execution context will benefit from Angular data-binding, exception handling, property watching, etc... You use $apply() to enter Angular execution context from JavaScript.
Keep in mind that in most places (controllers, services) $apply
has already been called for you by the directive which is handling the event. An explicit call to $apply
is needed only when implementing custom event callbacks, or when working with third-party library callbacks.
— AngularJS Developer Guide v1.1 - Concepts - Runtime
Hey What Mateus Koppe is showing it's a good answer, I would like to extend, just because this can help someone that needs to apply changes to the view.
The $Timeout service on angular refreshes the data on the view, but with setTimeout you don't have the same effect, so you should use $scope and $apply() to force the refresh on the screen.
On my EXAMPLE I use them, to show you the effect.
//Timeout angularjs
$timeout(function () {
console.log("poof! $timeout");
vm.showStuff = true;
}, 1000);
//Tiemout regular
setTimeout(function () {
console.log("poof1! setTimeout");
vm.showStuff1 = true;
}, 1000);
//Timeout + $scope.$apply
setTimeout(function () {
console.log("poof2! setTimeout");
vm.showStuff2 = true;
$scope.$apply(); //<<<<<<<<<<<<<<<<<<<<<<<<<<<
}, 3000);
I hope it helps.
As its explained on $apply()
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life-cycle of
exception handling, executing watches.
Also you should avoid using on $digest()
Usually, you don't call $digest() directly in controllers or in
directives. Instead, you should call $apply() (typically from within a
directive), which will force a $digest().
Please check my example here.
https://jsfiddle.net/moplin/x8mnwf5a/
As explained in previous answers it has to do with the $digest cycle, native setTimeout()
doesn't trigger the digest cycle and therefore the view doesn't get re-rendered with new value of vm.showStuff
.
I'd slightly extend Pablo Palacious's answer with the following usage of $scope.$apply(),
//Timeout + $scope.$apply
setTimeout(function () {
$scope.$apply(function () {
console.log("poof2! setTimeout");
vm.showStuff2 = true;
});
}, 3000);
You could also consider using $scope.$applyAsync()
which queues up expressions and functions for execution in the next digest cycle, as described here,
$applyAsync([exp]);
Schedule the invocation of $apply to occur at a later time. The actual time difference varies across browsers, but is typically around ~10 milliseconds.
This can be used to queue up multiple expressions which need to be evaluated in the same digest.