When using AngularJS's $timeout, what's the default delay? - javascript

On the remarkably brief AngularJS $timeout documentation page, the 'delay' argument is stated as optional. When using $timeout without specifying a delay, I note that a delay is still applied.
Can anyone tell me how much time is allotted for the delay when the argument is left implicit?

When $timeout delay is omitted, it defaults to 0. However, the block of code contained in it is executed after the DOM has been manipulated by Angular. See response to AngularJS $evalAsync vs $timeout

My understanding is that a delay of '0' means that it will be picked-up as part of the next run of the event loop. That's an especially short but indeterminate amount of time.

It's immediately executed, the default would be zero. Here is a jsfiddle showing it:
http://jsfiddle.net/dgarlitt/rqs3p/1/
angular
.module('myApp',[])
.controller('MyCtrl', function($scope, $timeout) {
$timeout(function() {
$scope.name = 'World';
});
});

The default delay is 0. The documentation has been updated since.
offical angularjs $timeout doc

Related

Execute Leaflet API after a $scope variable changed on angular

I have to bind js event on a element after it have been shown by changing a $scope variable using ng-show.
After I change the scope I run JS script but the Js is fastest than angular that he miss found some div not shown yet by ng-show.
Here is a plunker for some code sample to this problem. The solution I found for this kind of problem is to do the JS code in a setTimeout but I need a clean solution : https://plnkr.co/edit/iYDs552yCyd3R7ivgD84?p=preview
$scope.start = function(){
$scope.showMap=true;
setTimeout(function(){
jsFunc();
},1000);
}
Use the $timeout service:
$scope.start = function(){
$scope.showMap=true;
/*
setTimeout(function(){
jsFunc();
},1000);
*/
//Use $timeout
$timeout(jsFunc);
}
The $timeout service is integrated with the AngularJS digest cycle.
Since the Leaflet API is external to the AngularJS framework, a $timeout is necessary for the browser to render the DOM and execute the Leaflet API code.
For more information, see AngularJS $timeout Service API Reference.

How to properly use the AngularJS digest cycle

I've been looking into the caveats of the AngularJS Digest cycle and I would like to better understand where the separation is between using it properly and improperly.
For example, if I have an AngularJS code that looks like this:
var myApp = angular.module("testApp", []);
myApp.controller("testController", ["$scope", "$timeout", function($scope, $timeout){
setTimeout(function(){
$scope.username = "Test User name";
}, 3000);
$timeout(function(){
$scope.username = "AngularJS User name";
}, 3000);
}]);
Why is setTimeout not being used as part of the Digest Cycle, whereas $timeout is, and how can I fix this to work?
Please keep in mind, I am looking not only for a code solution but for an explanation of why this occurs. As good as a code solution may come, it will not explain itself.
$timeout is an angularized version of setTimeout(), i.e. it is coded in such a way that it triggers a digest cycle. setTimeout() is a plain Javascript function that knows nothing about Angular or a digest cycle. Since setTimeout() is not a simple JS object, Angular cannot $watch it.
So the whole point of having functionality like $timeout is that they are angular-friendly versions of some Javascript functionality.
$timeout() and setTimeout() are not the same, $timeout is angularised.
It uses promise ($q service) internally, which would resolve after every digest cycle, automatically. Whereas setTimeout(), is just a trick with registering your callback function in the queue.
If you want setTimeout() to be part of digest loop, call it within the $scope.$apply():
setTimeout(function(){
$scope.$apply(function(){
$scope.username = "Test User name";
});
}, 3000));
A more generic explanation is that setTimeout does not work inside of angular because it puts its callback on the event loop, which angular does not watch. The same situation would happen if you were to create an XMLHttpRequest directly instead of $http.
Angular has made their own abstraction of these utilities/objects so that when they finish, the digest cycle will pick up any changes.

Does AngularJS always know when something has changed, it's time to update?

I am reading AngularJS in Action by Lukas Ruebbelke to clear the concept of dirty checking as to how AngularJS works at a molecular level.
The author puts forward,
It is during the digest cycle that all watch expressions for a scope object
are evaluated. When a watch expression detects that a $scope property has
changed, then a listener function is fired.
Ocassionally a property is changed without AngularJS knowing about it. You
can manually kickstart a digest cycle vis $apply.
So, my question is what are those situations in a real web application when I need to kick off this digest cycle manually. And are those situations often seen? Kindly suggest.
This will come up any time an asynchronous callback returns from a non-angular library. e.g.
setTimeout(function() {
$scope.myVar = 1;
//Angular doesn't know when setTimeout finishes
//so you have to manually kick off a digest cycle.
$scope.$apply();
});
Angular has the $timeout service which takes care of starting a digest cycle for you but if you are using some third party library that takes a callback and doesn't have an angular wrapper then you will have to do this.
These situations can happen when using 3rd party libraries which provide some kind of data for example.
Say you use library-X which fires an event when something happened and new data is available, which you would like to render with AngularJS.
In these causes AngularJS does not know that data in the scope changed if you just directly set the variables.
That is why you should only modify scope variables inside the $apply function:
function MyController($scope) {
$scope.load = function() {
$scope.message = 'Loading...';
setTimeout(function() {
$scope.$apply(function () {
$scope.message = 'Finished loading!';
});
}, 2000);
}
}
It is also advised to use $scope.$apply(function () { /* update code */ }) instead of the single $scope.$apply() call, since it will properly catch errors and run the diggest regardless of any errors.

Why are these two updates no reflected in the view?

I'm trying to understand how to properly manipulate properties via a controller. The following code executes six updates over four seconds. Updates two and three are not reflected in the view. Why is this, and what do I need to do to have updates of those types affect the view?
Html
<div ng-controller="Controller">
myValue: <span ng-bind="myValue"></span>
</div>
Javascript
var app = angular.module('myApp', []);
app.controller('Controller', function ($scope, $interval) {
$scope.myValue = "first";
console.log($scope.myValue);
setTimeout(function() {
$scope.myValue = "second"; // never updates
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // never updates
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);
$interval(function() {
$scope.myValue = "fifth";
console.log($scope.myValue);
$interval(function() {
$scope.$emit("my-event", "sixth");
}, 1000, 1);
}, 3000, 1);
$scope.$on('my-event', function (event, arg) {
$scope.myValue = arg;
console.log(arg);
});
});
JSFiddle
Use $timeout instead of setTimeout to opt-in to the digest cycle. second won't show since the turn of the digest cycle overrides the value of myValue.
Updated fiddle: https://jsfiddle.net/d9gbpddy/4/
You can try {{myValue}} instead of a <span> element
So I obviously wasn't clear enough in the original question, as the upvoted answer (correctly) suggests using $timeout rather than setTimeout, however the original intent was to understand why the updates were not being reflected in the view, and what could be done to have these types of updates (that originate outside angular) affect the view as was intended.
Read the Scope guide
So whilst I chose to skip the Scopes section of the developer guide because it looked to be the most boring, it was probably the most important, and it clearly points out some items imperative to understanding how angular binds data, notably the Scope Life Cycle which notes;
When the browser calls into JavaScript the code executes outside the
Angular execution context, which means that Angular is unaware of
model modifications. To properly process model modifications the
execution has to enter the Angular execution context using the $apply
method. Only model modifications which execute inside the $apply
method will be properly accounted for by Angular.
There's an excellent answer here that further explains this concept. The first setence aptly reiterates the importance of understanding scope:
You need to be aware about how Angular works in order to understand
it.
Don't just call $scope.$apply
So you start adding calls to $scope.$apply around the place to cater for these things that originate outside angular, but then eventually you start getting:
Error: $digest already in progress
Which means you can't call $scope.$apply whilst $digest is executing. After which you may think, well how can I conditionally call $scope.$apply based on whether the $digest is currently running. But, you don't need to do that...
Just use $timeout
Hah, like the upvoted answer, I know, but based on a different thought process I think. See this answer. $timeout is not just being used in place of setTimeout, but rather is being used (without a delay) to wrap any model updates that are called from outside the Scope Life Cycle, and doing so ensures no conflict with any currently processing $digest.
Wrapping up
So, in the original code snippet, the second and third updates are not reflected in the view because they are performed outside the Angular execution context. The fact that third update doesn't affect the model also means that calling events outside the execution context doesn't get you into the execution context either.
The fourth update is already wrapped inside $interval, which itself causes the updates that code to be run on the next digest. Therefore, updating the code to show an example of an event outside the angular execution context that causes its updates to be shown in the view is as follows:
setTimeout(function() {
$timeout(function({ // start wrap
$scope.myValue = "second"; // now this updates!
console.log($scope.myValue);
$scope.$emit("my-event", "third"); // now this updates!
})); // end wrap
console.log($scope.myValue);
$interval(function() {
$scope.$emit('my-event', "fourth");
}, 1000, 1);
}, 1000);

AngularJs: double check in ng-show

Why checks of any expression in directives like ng-show (or other) are duplicate? I made simple jsfiddle example http://jsfiddle.net/fA5YX/. See in browser console.
<div ng-app ng-controller="ctrl" ng-show="test()">shown</div>
<script>
function ctrl($scope) {
$scope.test = function() {
console.log('check');
return true;
}
}
</script>
How make it stop? I need just one check.
Perhaps linked to Controller function getting called twice using ng-show - to quote:
At each digest cycle, for every watch, AngularJS evaluates the
associated expression to see if there's any change and if there is,
invoking the listener (in the case of ng-show/ng-hide, the listener
will show or hide the element based on the value returned by ready()).
Every watcher is run at the digest cycle. The digest cycle is repeated until none of the results has changed value (or when angular guards against infinite loop). This is why your watchers are run multiple times. Bottom line is, don't rely on watchers being fired only once because this goes against the basic groundrules of why angular works.
If you post details on what you try to achieve we might be able to guide you to a pattern that actually works in the angular philosophy.

Categories

Resources