window.setInterval() does no work properly in angular.js - javascript

i used setInterval() function in angular.js controller to show timer on UI later. create a scope variable which have value of inactivity in seconds.
setInterval(function(){ scope.timer++ }, 1000);
what it suppose to do is, it should update screen every second. but it doesn't. some time it update screen after two seconds, three seconds and sometime it update it every seconds, but its not consistent.
while when I log scope. timer value it behaves properly.
what would be the problem of this. Does angular.js restrict native javascript somehow or what?

Better is to use $interval, example:
var app = angular.module('MyApp', []);
app.controller('AppCtrl', function($scope, $interval) {
$scope.timer = 0;
$interval(function() { $scope.timer++; }, 1000);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<div ng-controller="AppCtrl" ng-cloak="" ng-app="MyApp">
<div ng-bind="timer"></div>
</div>

May be the value is updating in the controller but not in the DOM
try this
JS
setInterval(function(){
scope.timer++
setTimeout(function(){
$scope.$apply();
}, 1);
}, 1000);
Or you can use $interval and you are following my answer you can use $timeout instead of setTimeout also

You should use $interval
Update your code to this
$interval(function(){
$scope.timer++;
}, 1000)
Make sure that your $scope.timer is correctly initialized

You could try using Angular's $interval, from the docs
You have to add it to your controller and use it, like this ( view Plunker )
app.controller('testController', ['$scope', '$interval', function($scope, $interval) {
var count = 1;
$scope.count = count;
$interval(function() {
$scope.count++;
}, 1000);
}]);

Use scope.$apply
setInterval(function(){ scope.timer++; scope.$apply(); }, 1000);
setInterval is not angular function so scope is not refreshing.
Or use angular service ( prefered solution ):
$interval(function(){ scope.timer++;},1000);

setInterval is a JS native function, to execute it inside the digest loop you should call the apply() method or alternatively use the wrapper $interval as suggested in the AngularJS doc.
$interval(function(){ scope.timer++ }, 1000);
https://docs.angularjs.org/api/ng/service/$interval

setInterval is not an angular function, hence the scope is not refreshed, I DO NOT RECOMMEND using scope.$apply() as its wasteful.
use angular's inbuilt $interval method:
.controller('ExampleController', ['$scope', '$interval',
function($scope, $interval) {
stop = $interval(function() {
$scope.timer++;
}, 1000);
});
documentation: https://docs.angularjs.org/api/ng/service/$interval

Related

Angular - run `$digest` without having `$scope`

I have a controller without $scope
angular.module('todoApp', [])
.controller('TodoListController', function() {
var todoList = this;
todoList.title = "Default title";
setTimeout(function() {
todoList.title = "Another title will appear after 5 seconds";
}, 5000);
// ...some magic here
});
And view:
<h1>Current title: {{TodoListController.title}}</h1>
This code won't works correctly, becaufe function in setTimeout won't run $digest() which can update my TodoListController.title.
I know I can use $scope and use $scope.$digest(). But - is it possible to run $digest() without it? I have always access to object angular. Maybe through this object?
You should use $timeout instead of vanilla setTimeout.
angular.module('todoApp', [])
.controller('TodoListController', function($timeout) {
var todoList = this;
todoList.title = "Default title";
$timeout(function() {
todoList.title = "Another title will appear after 5 seconds";
}, 5000);
// ...some magic here
});
Using $timeout from angular will handle starting digest cycle.
Angulars $timeout is also useful if you want to notify angular to do updates without delay. In this case you can invoke it without second parameter.
$timeout(function(){
//something outside angular ...
});
Function passed to $timeout will be invoked on next digest cycle.
This way is better than calling $digest manually because it will prevent digest already in progress errors.
You must use the angular version of timeout: $timeout.
https://docs.angularjs.org/api/ng/service/$timeout
This service trigger the angular $digest process.

clear Interval not working

this is my controller:
.controller('mainController', function($http, $scope, $timeout, $sce, updateService) {
updateData = function() {
updateService.getDataA($http, $scope);
updateService.getDataB($http, $scope, $sce);
}
var updateIntervalId = setInterval(updateData, 1000);
})
Now when the user refreshes the page old requests are still being made, and I tried to cancel it by putting this under the interval call:
window.onbeforeunload = function() {
console.log('interval killed');
clearInterval(updateIntervalId);
}
the console is logging 'interval killed', so its being executed, but requests are still being made, why?
Let us do the angular way
Inject $interval service in your controller
And update your code to following
var intervalPromise = $interval(updateData, 1000);
$scope.$on('$destroy', function() { // Gets triggered when the scope is destroyed.
$interval.cancel(intervalPromise );
});
And remove window.onbeforeunload event handler.
When it comes to angularJS I would always recommend to use $interval to handle intervals, since it works better with the angularJS hidden mechanics.
Then if the event window.onbeforeunload lies outside the controller, you will never be able to access updateIntervalId since it lies inside another scope, hence you should make it global.

callback function gives an error - angularjs

I am using the $timeout service in angular to decrease a variable from 100 down to 1 every 1/10 seconds.
I know there is a much easier way to do this using the $interval service and I've done it. But, here, specifically, I am testing a callback in angular - not really trying to decrease a variable from 100 down to 1 every 1/10 seconds.
Specifically, I want the callback to be called when the $timeout function is sleeping (for 1/10 seconds)
My question is why I get an "undefined is not a function" error when using a callback. I have included the JS code below. It's also on JSbin here
Here's the JS code:
var testApp = angular.module('testApp', []);
testApp.controller('testCtrl', ['$scope', '$timeout', function($scope, $timeout) {
var q = 100;
function decreaseQ(callback) {
$scope.someThing = q;
if(q>0){ $timeout(decreaseQ, 100);q--;}
callback();
}
decreaseQ(function(){console.log("Hello World");});
}]);
That is because after the first invocation of decreaseQ with a function argument you are calling decreaseQ without any argument by setting it in the timeout.But you expect a callback and you invoke the function decreaseQ() which causes the error.
Instead you would just need to pass the function as callback.
if (q-- > 0) {
$timeout(function() {
decreaseQ(callback); //Pass the callback as argument
}, 100);
}
Or use bound function
$timeout(angular.bind(null, decreaseQ, callback), 100);
or just do not run if there is no callback provided by adding a check.
if(angular.isFunction(callback)) { callback(); }
Or use the dummy anonymous function
(callback||angular.noop)();

error when rootScope updated

I have an Angular service that updates the $rootScope. The updating actually works fine, but it throws an error in the console which worries me.
app.service("scroll", function($rootScope, $window) {
this.scrolling = function(delta){
$rootScope.scroll.current -= delta;
}
$rootScope.$apply();
});
If I remove the $rootScope.$apply() the error doesn't appear, but then the rootScope value doesn't seem to be updated when I refer to it in my HTML.
For example in my HTML:
{{scroll.current}}
This only updates when I use $rootScope.$apply(). Is there a better way to update the $rootScope, or am I just doing something wrong?
Error being thrown:
Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.0-rc.5/$rootScope/inprog?p0=%24apply
As $rootScope.$apply() trigger a $digest and in angular at any point in time there can be only one $digest or $apply operation in progress.
Use $timeout.
app.service("scroll", function($rootScope, $window, $timeout) {
this.scrolling = function(delta) {
$rootScope.scroll.current -= delta;
}
$timeout(function() {
$rootScope.$apply(); //this triggers a $digest
}, 1);
});
Probably your function:
$rootScope.$apply();
is called also when it is not needed.
You just have to add this line:
if ( !$rootScope.$$phase )
$rootScope.$apply()
$$phase is when angular is digesting
EDIT: corrected $rootScope.apply() to $rootScope.$apply()

why the need to use 'timeout' in angular

This is probably a total newb question...apologies, but I can't get my head around it.
In a lot of angular documentation/examples I see asynchronous functions wrapped in 'timeout' blocks. Many are wrapped in setTimeout() and require the explicit use of
if (!$scope.$$phase) {
$scope.$apply();
}
Given that angular provides $timeout, the above code just seems outdated or wrong and within angular the use of $timeout should always be preferred. However, I digress.
Here is a snippet of some example code taken from: http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/
var myModule = angular.module('myModule', []);
// From this point on, we'll attach everything to 'myModule'
myModule.factory('HelloWorld', function($timeout) {
var getMessages = function(callback) {
$timeout(function() {
callback(['Hello', 'world!']);
}, 2000);
};
return {
getMessages: getMessages
};
});
I see this wrapping of code in timeout blocks everywhere particularly related to asynchronous calls. But can someone explain why this is needed? Why not just change the code above to:
var myModule = angular.module('myModule', []);
// From this point on, we'll attach everything to 'myModule'
myModule.factory('HelloWorld', function() {
var getMessages = function(callback) {
callback(['Hello', 'world!']);
};
return {
getMessages: getMessages
};
});
Why wouldn't the code snippet above work just fine?
The use of $timeout or $interval is to implicitly trigger a digest cycle. The process is as follows:
Execute each task in the callback function
Call $apply after each task is executed
$apply triggers a digest cycle
An alternative is to inject $rootScope and call $rootScope.$digest() if you are using services that don't trigger a $digest cycle.
Angular uses a dirty-checking digest mechanism to monitor and update values of the scope during
the processing of your application. The digest works by checking all the values that are being
watched against their previous value and running any watch handlers that have been defined for those
values that have changed.
This digest mechanism is triggered by calling $digest on a scope object. Normally you do not need
to trigger a digest manually, because every external action that can trigger changes in your
application, such as mouse events, timeouts or server responses, wrap the Angular application code
in a block of code that will run $digest when the code completes.
References
AngularJS source: intervalSpec.js
AngularJS source: timeoutSpec.js
$q deferred.resolve() works only after $timeout.flush()
AngularJS Documentation for inprog | Digest Phases
The $timeout in your example is probably used just to simulate an async function, like $http.get. As to why $timeout and not setTimeout: $timeout automatically tells angular to update the model, without the need to call $scope.$apply()
Also, consider the following example:
$scope.func = function(){
$scope.showSomeLoadingThing = true;
//Do some long-running stuff
$scope.showSomeLoadingThing = false;
}
No loading thingy will be shown, you would have to write it like this:
$scope.func = function(){
$scope.showSomeLoadingThing = true;
$timeout(function(){
//Do some long-running stuff
$scope.showSomeLoadingThing = false;
});
}

Categories