AngularJS: Executing a function after timeout ends - javascript

I have this angularJS app which has a countdown timer. I want to execute a function after it becomes zero. It currently goes into negative after it reaches 0. How to achieve this?
Angular Code:
myApp.controller('MyController', ['$scope', '$timeout' ,function($scope, $http, $routeParams, $timeout) {
$scope.counter = 5000;
$scope.onTimeout = function(){
$scope.counter--;
mytimeout = $timeout($scope.onTimeout,1000);
}
var mytimeout = $timeout($scope.onTimeout,1000);
}
}]);
testControllers.filter('formatTimer', function() {
return function(input)
{
function z(n) {return (n<10? '0' : '') + n;}
var seconds = input % 60;
var minutes = Math.floor(input / 60);
var hours = Math.floor(minutes / 60);
return (z(hours) +':'+z(minutes)+':'+z(seconds));
};
});
HTML:
<div><p>{{counter|formatTimer}}</p></div>

Simply stop calling it when the counter is 0:
$scope.onTimeout = function(){
if (--$scope.counter > 0) {
$timeout($scope.onTimeout, 1000);
} else {
// Call your function now that counter is 0
}
}

Depending on what else you are doing, you probably don't need to put your timeout method on the scope. Also, look at $interval instead. It's better suited for calling something continuously.
function decreaseCounter() {
if(--$scope.counter <= 0) {
$interval.cancel(intervalPromise);
yourOtherFunction();
}
}
var intervalPromise = $interval(decreaseCounter, 1000);
https://docs.angularjs.org/api/ng/service/$interval

If you need a precise countdown (specially if the time span is large) you need to calculate the time spent using Date time function. Simply incrementing a counter each 1000 milliseconds 5000 times, doesen't guarantee that at the end, the real time spent will be exactly 5000 seconds!
You could try something like this:
Controller:
function MyCtrl($scope, $interval) {
var intervalId;
$scope.counter = 0;
$scope.initialCountdown = 10;
$scope.countdown = $scope.initialCountdown;
$scope.timer = function(){
var startTime = new Date();
intervalId = $interval(function(){
var actualTime = new Date();
$scope.counter = Math.floor((actualTime - startTime) / 1000);
$scope.countdown = $scope.initialCountdown - $scope.counter;
}, 1000);
};
$scope.$watch('countdown', function(countdown){
if (countdown === 0){
$scope.stop();
}
});
$scope.start = function(){
$scope.timer();
};
$scope.stop = function(){
$interval.cancel(intervalId);
};
}
View:
<div ng-controller="MyCtrl">
<div>Counter: {{counter}}</div>
<div>Countdown in seconds: {{countdown}}</div>
<div>Countdown date time: {{countdown | secondsToDateTime | date:'HH:mm:ss'}}</div>
<button ng-click="start()">start</button>
<button ng-click="stop()">stop</button>
</div>
Filter secondsToDateTime:
myApp.filter('secondsToDateTime', [function() {
return function(seconds) {
return new Date(1970, 0, 1).setSeconds(seconds);
};
}])
CHECK THE DEMO FIDDLE
Enjoy!

Related

Timer binding not working ionic

I have created a stopwatch factory service, doesn't really do much besides running (reset and other features not implemented, just ignore them).
I set a $scope.time to receive the changes of the timer, however it isn't being updated.
I have tried many different approaches the last one I guess is really bad practice but I wanted to try and make it work.
my view:
<ion-view view-title="Clock">
<ion-content>
<div ng-controller="controller as ClockCtrl">
<h1 ng-bind="ClockCtrl.time"></h1>
</div>
</ion-content>
</ion-view>
my Controller:
.controller('ClockCtrl', function($scope, Stopwatch) {
var vm = $scope;
$scope.time = "";
Stopwatch.init($scope);
})
my service:
factory('Stopwatch', function() {
var Stopwatch = (function() {
var s;
var isRunning = 0;
var time = 0;
var thatElement;
function run(element){
if(isRunning !== 0){
time++;
s.mills = time % 10;
s.secs = Math.floor(time / 10);
s.mins = Math.floor(time / 10 / 60);
//Stopwatch.currentTime = ("0"+s.mins).slice(-2)+":"+("0"+s.secs).slice(-2)+":"+("0"+s.mills).slice(-2);
thatElement.time = ("0"+s.mins).slice(-2)+":"+("0"+s.secs).slice(-2)+":"+("0"+s.mills).slice(-2);
setTimeout(run, 100);
}
};
return {
settings: {
currTime: "",
mills: 0,
secs: 0,
mins: 0,
i: 1,
times: ["00:00:00"],
},
currentTime: "",
init: function(element) {
thatElement = element;
s = this.settings;
this.start(element);
},
clear: function() {
s.i = 1,
s.mins = s.secs = s.mills = 0;
s.times = ["00:00:00"],
s.results.innerHTML = s.clearButton;
},
start:function(element){
isRunning = 1;
run(element);
},
stop: function(){
isRunning = 0;
}
}
})();
return Stopwatch;
})
I know that the last approach is bad, I would accept any pointers, this is my first time using ionic, 2nd time trying angular.
Thanks in advance!
Since you are using controllerAs syntax you need to set controller instance property, not $scope object one:
.controller('ClockCtrl', function($scope, Stopwatch) {
this.time = "";
Stopwatch.init(this);
});

How to make a seconds countdown using angular?

I'm looking on how to make a 60 seconds countdown using angular js.
I want to show the countdown on the page ! and when the countdown is finished, the controller should reload to execute the code again ! and get the update json object !
my controller looks like :
.controller('todaymatches', function($rootScope,$scope, $http) {
$http.get("http://www.domaine.com/updatedjson/")
.success(function (response) {
$scope.matches = response;
});
})
I'm made a code ! I'm not sure if this works properly ! anyway it's not working on my app.
$scope.countdown = function() {
stopped = $timeout(function() {
console.log($scope.counter);
$scope.counter--;
$scope.countdown();
}, 1000);
};
Here is a simple countdown example:
HTML
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc11.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
</head>
<body>
<div ng-controller="CountdownController">
{{counter}}
</div>
</body>
</html>
Javascript
function CountdownController($scope,$timeout) {
$scope.counter = 60;
$scope.onTimeout = function(){
if ($scope.counter > 0) {
$scope.counter--;
mytimeout = $timeout($scope.onTimeout,1000);
} else {
$scope.counter = 60;
}
}
var mytimeout = $timeout($scope.onTimeout,1000);
}
Demo
'use strict';
var ngApp = angular.module('myApp', ['Test']);
var c1 = angular.module('Test', []);
c1.controller('Ctrl1', function ($scope, $timeout) {
$scope.coutDown = function () {
$scope.onTimeout = function () {
console.log("value", $scope.value);
$scope.value = $scope.value - 1;
return $scope.coutDown($scope.value);
};
var delay = $timeout($scope.onTimeout, 1000);
if ($scope.value < 1) {
$timeout.cancel(delay);
return true;
}
return false;
};
$scope.value = 5;
$scope.coutDown();
});
<div ng-app="myApp">
<div ng-controller="Ctrl1">
<h1>{{value}}</h1>
</div>
</div>
http://jsfiddle.net/pbxaD/49/
if you want to use $timeout you have to inject it. But why don't you just call the update method in a certain interval?
.controller('todaymatches', function($rootScope,$scope, $http, $interval) {
var update = function() {
$http.get("http://www.domaine.com/updatedjson/")
.success(function (response) {
$scope.matches = response;
});
};
var initialize = function() {
$interval(function() {
update();
}, 60 * 1000)
};
initialize();
})
I tried this for the count down and it seems to work.
app.controller('CountDownController', function($scope, $timeout) {
$scope.counter = 60;
$scope.countdown = function() {
if ($scope.counter === 0) {
// do your reload and execute here
//Just reset the counter if you just want it to count again
$scope.counter = 60;
return;
} else {
$timeout(function() {
console.log($scope.counter);
$scope.counter--;
$scope.countdown();
}, 1000);
}
};
$scope.countdown();
});
You could tie up the various things you want to do inside the if condition of the above code as commented. I just reset the counter after counting down to 0.

JS- Countdown Timer Alerts

Need some help with my code, I can't get my alerts to work with my countdown timer. They should be alerting at 4,3,2 minutes left on the timer. I currently can't get the alerts to fire at all, sometimes they would fire but each second after 4, the alert for "4" would fire. I need it to just go once... Any help would be appreciated
Heres my script
var running=false
var endTime=null
var timerID=null
function startTimer(){
running=true
now=new Date()
now=now.getTime()
endTime=now+(1000*60*5)
showCountDown()
}
function showCountDown(){
var now=new Date()
now=now.getTime()
if (endTime-now<=239990 && endTime-now>240010){alert("4")};
if (endTime-now<=179990 && endTime-now>180010){alert("3")};
if (endTime-now<=119990 && endTime-now>120010){alert("2")};
if (endTime-now<=0){
stopTimer()
alert("Time is up. Put down pencils")
} else {
var delta=new Date(endTime-now)
var theMin=delta.getMinutes()
var theSec=delta.getSeconds()
var theTime=theMin
theTime+=((theSec<10)?":0" : ":")+theSec
document.forms[0].timerDisplay.value=theTime
if (running){
timeID=setTimeout("showCountDown()",1000)
}
}
}
function stopTimer(){
clearTimeout(timeID)
running=false
document.forms[0].timerDisplay.value="0.00"
}
Update, Sorry meant minutes instead of seconds
Update 2: Change the ifs, now they fire but keep firing after the 4 second mark
if (endTime-now<=240010 && endTime-now<=239990){alert("4")};
if (endTime-now<=180010 && endTime-now<=179990){alert("3")};
if (endTime-now<=120010 && endTime-now<=119990){alert("2")};
Why are you calling clearTimeout? setTimeout invokes its callback only once. There is no need to clear it. Also you could just have a variable that stores the minutes until the end of the countdown and decrement that by one in each iteration.
The simplest solution might look like this
function startTimer(minutesToEnd) {
if(minutesToEnd > 0) {
if(minutesToEnd <= 4) {
console.log(minutesToEnd);
}
setTimeout(startTimer, 60000, minutesToEnd - 1);
} else {
console.log("Time is up. Put down pencils")
}
}
I actually spent some time working on this. I have no idea if this is what you wanted, but I created a timer library. I have a working demo for you. I had fun making this. Let me know what you think:
JS:
(function () {
var t = function (o) {
if (!(this instanceof t)) {
return new t(o);
}
this.target = o.target || null;
this.message = o.message;
this.endMessage = o.endMessage;
//setInterval id
this.si = -1;
//Initial start and end
this.startTime = null;
this.endTime = null;
this.interTime = null;
this.duration = o.duration || 1000 * 60 * 5;
//looping speed miliseconds it is best to put the loop at a faster speed so it doesn't miss out on something
this.loop = o.loop || 300;
//showing results miliseconds
this.show = o.show || 1000;
};
t.fn = t.prototype = {
init: function () {}
};
//exporting
window.t = t;
})();
//Timer Functions ---
t.fn.start = function () {
this.startTime = new Date();
this.interTime = this.startTime.getTime();
this.endTime = new Date().setMilliseconds(this.startTime.getMilliseconds() + this.duration);
//returns undefined... for some reason.
console.log(this.endTime);
var $this = this;
this.writeMessage(this.duration);
this.si = setInterval(function () {
var current = new Date(),
milli = current.getTime();
if (milli - $this.interTime >= $this.show) {
var left = $this.endTime- milli;
if (left <= 0) {
$this.stop();
} else {
$this.interTime = milli;
$this.writeMessage(left);
}
}
}, this.loop);
return this;
};
t.fn.writeMessage = function(left){
this.target.innerHTML = this.message + ' ' + Math.floor(left / 1000);
return this;
};
t.fn.stop = function () {
//stopping the timer
clearInterval(this.si);
this.target.innerHTML = this.endMessage;
return this;
};
//Not chainable
t.fn.isRunning = function () {
return this.timer > -1;
};
var timer = t({
target: document.getElementById('results'),
loop: 50,
duration: 10000,
show: 1000, //default is at 1000 miliseconds
message: 'Time left: ', //If this is ommited then only the time left will be shown
endMessage: 'Time is up. Put down your pencils'
}).start();
document.getElementById('stop').onclick = function(){
timer.stop();
};
HTML:
<div id="results"></div>
<button id="stop">Stop</button>
Demo here
Update: I added some stuff
Demo 2
Update 2: I fixed the bug where 10 would hop straight to 8
Demo 3

Binding an Angular service to view

I created a countdown clock as part of a larger project. Here is the code for the service
'use strict';
angular.module('myApp')
.service('Countdownclock', function Countdownclock() {
var secondsRemaining = 0;
var timerProcess;
return {
//initialize the clock
startClock: function(minutes) {
secondsRemaining = minutes * 60;
timerProcess = setInterval(this.timer, 1000);
},
//timer function
timer: function() {
secondsRemaining -= 1;
if (secondsRemaining <= 0) {
clearInterval(timerProcess);
}
},
//get time
getTime: function() {
return secondsRemaining;
},
//add time
addTime: function(seconds) {
secondsRemaining += seconds;
},
//stop clock
stopClock: function() {
secondsRemaining = 0;
clearInterval(timerProcess);
}
};
});
Then I call it to a from a controller which is also linked to a view
'use strict';
angular.module('myApp')
.controller('MainCtrl', function($scope, Countdownclock) {
Countdownclock.startClock(1);
$scope.seconds = Countdownclock.getTime();
$scope.$watch(Countdownclock.getTime(), function(seconds) {
$scope.seconds = Countdownclock.getTime();
});
});
For some reason I can't figure out how to bind secondsRemaining to $scope.seconds. I've been trying to figure this thing out for about an hour. I'm not exactly a wiz at functional programing so I have a feeling I'm just thinking about it wrong.
Inject $interval into your service and replace setInterval with it:
timerProcess = $interval(this.timer, 1000);
If you want to use a watcher you can register it like this:
$scope.$watch(function () { return Countdownclock.getTime(); }, function (newValue, oldValue) {
// Might be identical when called due to initialization - Good to know for some cases
if (newValue !== oldValue) {
$scope.seconds = newValue;
}
});
Demo: http://plnkr.co/edit/usUoOtWMwoDRht27joOA?p=preview
You can use a function instead:
$scope.seconds = function() { return Countdownclock.getTime() };
Then remove the
$scope.$watch(Countdownclock.getTime(), function(seconds) {
$scope.seconds = Countdownclock.getTime();
});
You can then use it in your template like this:
<div>{{seconds()}}</div>
But first, like Spock said, you have to use $interval instead of setInterval.

how do I pause and resume a timer?

I got this function that starts a timer on this format 00:00:00 whenever I click on a button. But I don't know how to do functions resume and pause. I've found some snippets that I thought could be helpful but I couldn't make those work. I'm new to using objects in js.
function clock() {
var pauseObj = new Object();
var totalSeconds = 0;
var delay = setInterval(setTime, 1000);
function setTime() {
var ctr;
$(".icon-play").each(function () {
if ($(this).parent().hasClass('hide')) ctr = ($(this).attr('id')).split('_');
});
++totalSeconds;
$("#hour_" + ctr[1]).text(pad(Math.floor(totalSeconds / 3600)));
$("#min_" + ctr[1]).text(pad(Math.floor((totalSeconds / 60) % 60)));
$("#sec_" + ctr[1]).text(pad(parseInt(totalSeconds % 60)));
}
}
pad() just adds leading zeros
I think it will be better if you will create clock object. See code (see Demo: http://jsfiddle.net/f9X6J/):
var Clock = {
totalSeconds: 0,
start: function () {
var self = this;
this.interval = setInterval(function () {
self.totalSeconds += 1;
$("#hour").text(Math.floor(self.totalSeconds / 3600));
$("#min").text(Math.floor(self.totalSeconds / 60 % 60));
$("#sec").text(parseInt(self.totalSeconds % 60));
}, 1000);
},
pause: function () {
clearInterval(this.interval);
delete this.interval;
},
resume: function () {
if (!this.interval) this.start();
}
};
Clock.start();
$('#pauseButton').click(function () { Clock.pause(); });
$('#resumeButton').click(function () { Clock.resume(); });
Just clearing the interval wouldn't work, because totalSeconds would not get incremented.
I would set up a flag that determines if the clock is paused or not.
This flag would be simply set upon calling pause() or unset upon resume().
I separated the totalSeconds increase to a 'tick' timeout that will always be running, even when paused (so that we can keep track of the time when we resume).
The tick function will therefore only update the time if the clock is not paused.
function clock()
{
var pauseObj = new Object();
var totalSeconds = 0;
var isPaused = false;
var delay = setInterval(tick, 1000);
function pause()
{
isPaused = true;
}
function resume()
{
isPaused = false;
}
function setTime()
{
var ctr;
$(".icon-play").each(function(){
if( $(this).parent().hasClass('hide') )
ctr = ($(this).attr('id')).split('_');
});
$("#hour_" + ctr[1]).text(pad(Math.floor(totalSeconds/3600)));
$("#min_" + ctr[1]).text(pad( Math.floor((totalSeconds/60)%60)));
$("#sec_" + ctr[1]).text(pad(parseInt(totalSeconds%60)));
}
function tick()
{
++totalSeconds;
if (!isPaused)
setTime();
}
}
Use window.clearInterval to cancel repeated action which was set up using setInterval().
clearInterval(delay);
<html>
<head><title>Timer</title>
<script>
//Evaluate the expression at the specified interval using setInterval()
var a = setInterval(function(){disp()},1000);
//Write the display() for timer
function disp()
{
var x = new Date();
//locale is used to return the Date object as string
x= x.toLocaleTimeString();
//Get the element by ID in disp()
document.getElementById("x").innerHTML=x;
}
function stop()
{
//clearInterval() is used to pause the timer
clearInterval(a);
}
function start()
{
//setInterval() is used to resume the timer
a=setInterval(function(){disp()},1000);
}
</script>
</head>
<body>
<p id = "x"></p>
<button onclick = "stop()"> Pause </button>
<button onclick = "start()"> Resume </button>
</body>
</html>

Categories