setState not applying changes in React function component - reactjs

Im trying to do a Pomodoro Clock timer, which is basically two timers that alternate between.
Thats all the code:
import React, { useState } from "react";
function Challenge20() {
const [timer, setTimer] = useState('');
let minutes = 0;
let seconds = 0;
const [workRest, setWorkRest] = useState('work');
function startTimer() {
document.getElementById('start').style.display = 'none';
minutes = document.getElementById('work').value - 1;
seconds = 59;
setInterval(reduceSeconds, 1000);
};
function reduceSeconds() {
if (seconds < 10) {
setTimer(minutes + ':' + '0' + seconds);
}
else {
setTimer(minutes + ':' + seconds);
}
seconds -= 1;
if (seconds < 1 && minutes > 0) {
seconds = 59;
minutes -= 1;
}
else if (seconds == 0 && minutes == 0){
setWorkRest(workRest == 'work' ? 'rest' : 'work');
minutes = document.getElementById(workRest == 'work' ? 'work' : 'rest').value;
}
};
return (
<>
<label>Work Minutes:</label>
<input id='work' type='number' max='60'/>
<br/>
<label>Rest Minutes:</label>
<input id='rest' type='number' max='60'/>
<br/>
<br/>
<span id='timer'>{workRest} -> {timer}</span>
<button id='start' onClick={() => startTimer()}>Start!</button>
</>
);
};
export default Challenge20;
The problem is in this part:
else if (seconds == 0 && minutes == 0){
setWorkRest(workRest == 'work' ? 'rest' : 'work');
minutes = document.getElementById(workRest == 'work' ? 'work' : 'rest').value;
}
The setState is not changing from 'work' to 'rest', also tried to call a function to change the state, clearing interval and 2 separated if, nothing worked, what am I doing wrong?

useState is not work inside the condition. For ex: you are set the state value in the if condition. State value not updated in condition.

I think this is what you're trying to achieve? The problem is that the timer keeps going. On the next iteration, it sets workRest back to its previous value. To solve this, I used clearInterval to stop iterating, and decremented seconds to display 00:00 on the timer. As such, I had to assign the interval creation to a variable we can pass into clearInterval.
import React, { useState } from "react";
function Challenge20() {
const [timer, setTimer] = useState("");
let minutes = 0;
let seconds = 0;
const [workRest, setWorkRest] = useState("work");
let interval;
function startTimer() {
document.getElementById("start").style.display = "none";
minutes = document.getElementById("work").value - 1;
seconds = 59;
interval = setInterval(reduceSeconds, 1);
}
function reduceSeconds() {
if (seconds < 10) {
setTimer(minutes + ":" + "0" + seconds);
} else {
setTimer(minutes + ":" + seconds);
}
seconds -= 1;
if (seconds < 1 && minutes > 0) {
seconds = 59;
minutes -= 1;
} else if (seconds == 0 && minutes == 0) {
console.log();
setWorkRest(workRest == "work" ? "rest" : "work");
minutes = document.getElementById(workRest == "work" ? "work" : "rest")
.value;
clearInterval(interval);
seconds -= 1;
}
}
return (
<>
<label>Work Minutes:</label>
<input id="work" type="number" max="60" />
<br />
<label>Rest Minutes:</label>
<input id="rest" type="number" max="60" />
<br />
<br />
<span id="timer">
{workRest} -> {timer}
</span>
<button id="start" onClick={() => startTimer()}>
Start!
</button>
</>
);
}

Related

React setState and use the value to update another state

I have a simple roll the dice code and I want to update the score based on the dice roll.
This is the states of the dices and the scores:
const [Dice1, setDice1] = useState(0);
const [Dice2, setDice2] = useState(0);
const [Score1, setScore1] = useState(0);
const [Score2, setScore2] = useState(0);
function randomNum() {
setDice2(Math.floor(Math.random() * 6) + 1);
setDice1(Math.floor(Math.random() * 6) + 1);
Dice1 > Dice2 && Dice1 !== Dice2 ? setScore1(Score1 + 1) : Dice2 > Dice1 && Dice1 !== Dice2 ? setScore2(Score2 + 1) : setScore2(Score2 + 0);
}
The function randomNum is triggered on click.
return (
<div className="App">
<header className="App-header">
<p>Player 1 rolls {Dice1}</p>
<p>Player 2 rolls {Dice2}</p>
<button onClick={randomNum}>Roll the dice</button>
{Dice1 > Dice2 ? <p>Player 1 win</p> : Dice1 === Dice2 ? <p>Draw</p> : <p>Player 2 win</p>}
<p>Player 1 score is {Score1} points</p>
<p>Player 2 score is {Score2} points</p>
<button onClick={resetScore}>Reset score</button>
</header>
</div>
);
Everything works well, except that the score if updated with 1 round lag.
Everytime I roll, it is added the point from the last round.
What am I'm doing wrong here?
Update
One Score state would look like
const [score, setScore] = useState({
score1: 0,
score2: 0
});
You can update like
setScore(prev=>{...prev, score1: prev.score1 + 1})
setScore(prev=>{...prev, score2: prev.score2 + 1})
Try this
Use one state for dice instead
const [dice, setDice] = useState({
dice1: 0,
dice2: 0,
})
Update the state
function randomNum() {
const dice1Val = Math.floor(Math.random() * 6) + 1;
const dice2val = Math.floor(Math.random() * 6) + 1;
setDice({
dice1: dice1Val,
dice2val: dice2Val
})
And since setting score is an effect of setting dice, update the score in a useEffect
useEffect(() => {
dice.dice1 > dice.dice2 && dice.dice1 !== dice.dice2 ? setScore1(prev => prev + 1); : dice.dice2 > dice.dice1 && dice.dice1 !== dice.dice2 ? setScore2(prev => prev + 1) : setScore2(prev => prev + 1)
},[dice])
}
You can also put the scores under one state instead.
The most easiest way I can think of is to implement the resetScore function that will set the both states to 0.
function resetScore() { setScore1(0); setScore2(0); }
or call this function at the start of the randomNum.
You are not using the previous state values correctly. It is not recommended to use previous state values directly while updating state variables.
So in your code instead of :
setScore1(Score1 + 1)
you should do like this :
setScore1(prevScore => prevScore + 1);
and similarly for other state variables.

Conditional unique rendering

I have an array with objects sorted with a date entry and a component to list in what approximated time it was listed.
const TimeHeader = ({date}) => {
const calculateDays = (date) => {
let timeDiff = new Date().getTime() - new Date(date).getTime();
let days = Math.ceil(timeDiff / (1000 * 3600 * 24));
return days;
}
let daysGone = calculateDays(date);
let color = "green";
let text = "Less than 1 Week";
if (daysGone > 7 && daysGone <= 30){
color = "blue";
text = "Less than 1 Month";
} else if (daysGone > 30 && daysGone <= 90){
color = "yellow";
text = "Less than 3 Months";
} else if (daysGone > 90){
color = "red";
text = "More than 3 Months";
}
return (
<div style={{backgroundColor: color}}>{text}</div>
)
}
How would I call upon this component to be rendered only once for each instance? (Once for Less than 1 week, once for less than one month etc)
Right now I am just calling it before each item listing but of course it leads a lot of repetition.
{list.map(item => {
return (
<div key={item.id}>
<TimeHeader date={item.date} />
<ItemDisplay item={item} />
</div>
)
})}
One solution would be to split the array to different categories beforehand, but I'm wondering if there is a nice solution that doesn't require splitting the array.

Audio does not play on localhost, but works perfectly on codepen

I have written a 25 + 5 clock as a project on freeCodeCamp. It works well on codeine and plays the audio as it should. However, when I try to play it locally on the create-react-app I made the audio doesn't play when timer hits zero. I decided to test out the audio using onClick and it works then. I think it has to do something with the fact that it is being called during useEffect, but I am not sure. Any help is appreciated!
The error that shows up after the timer hits zero is this.
×
TypeError: Attempted to assign to readonly property.
playSound
src/index.js:115
112 | function playSound() {
113 | const audio = document.getElementById("beep");
114 | audio.removeAttribute("readonly");
> 115 | audio.duration = 1; // Need to pass first audio test
| ^ 116 | audio.play();
117 | }
118 | return (
Here is the code for everyone to see.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./index.css";
function Timer() {
const [breakLength, setBreakLength] = useState(5);
const [sessionLength, setSessionLength] = useState(25);
const [timer, setTimer] = useState(1500); // 1500 is 25 minutes, but in seconds
const [timerType, setTimerType] = useState("session");
const [timerState, setTimerState] = useState("stopped");
const [timeoutId, setTimeoutId] = useState("");
React.useEffect(() => {
// If timer is > 0 and the timerState is clicked to running
if (timer > 0 && timerState === "running") {
setTimeoutId(setTimeout(() => setTimer(timer - 1), 1000)); // Must use setTimeout as it only triggers expression once. setInterval will continuously call expression until told otherwise.
}
// If session timer ends.
else if (timer === 0 && timerType === "session") {
setTimerType("break"); // Change timer type back to break
setTimer(breakLength * 60); // Multiply by 60 because we need to convert minutes into seconds
playSound();
}
// If break timer ends
else if (timer === 0 && timerType === "break") {
setTimerType("session"); // Change timer type break
setTimer(sessionLength * 60); // Multiply by 60 because we need to convert minutes into seconds
playSound();
}
clearTimeout(timeoutId);
}, [timer, timerState, timerType, breakLength, sessionLength]);
function resetClick() {
// simply reset all states and audio must be paused then set back to beginning with currentTime = 0
setTimerState("stopped");
setBreakLength(5);
setSessionLength(25);
setTimerType("session");
setTimer(1500);
const audio = document.getElementById("beep");
audio.pause();
audio.currentTime = 0;
}
function decrementBreakClick() {
// Doesn't let break length go below 1 and timer must be stopped
if (breakLength > 1 && timerState === "stopped") {
setBreakLength(breakLength - 1);
}
}
function incrementBreakClick() {
// Doesn't let break length go above 60 and timer must be stopped
if (breakLength < 60 && timerState === "stopped") {
setBreakLength(breakLength + 1);
}
}
function decrementSessionClick() {
// Doesn't let session length go below 1 and timer must be stopped
if (sessionLength > 1 && timerState === "stopped") {
setSessionLength(sessionLength - 1);
setTimer(timer - 60); // Take away 60 as timer is set in seconds.
}
}
function incrementSessionClick() {
// Doesn't let session length go below 1 and timer must be stopped
if (sessionLength < 60 && timerState === "stopped") {
setSessionLength(sessionLength + 1);
setTimer(timer + 60); // Add 60 as timer is set in seconds.
}
}
function startStopClick() {
// if state is stopped then change it to running. Else change running back to stopped
if (timerState === "stopped") {
setTimerState("running");
} else {
setTimerState("stopped");
}
}
// Convert the timer state that is in seconds to minutes and seconds.
function timerToClock() {
let minutes = Math.floor(timer / 60);
let seconds = timer - minutes * 60;
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
return minutes + ":" + seconds;
}
function playSound() {
const audio = document.getElementById("beep");
audio.removeAttribute("readonly");
audio.duration = 1; // Need to pass first audio test
audio.play();
}
return (
<div>
<h1 id="header">25 + 5 Clock</h1>
<div id="machine-container">
<div id="break-session-containter">
<BreakLength
breakLength={breakLength}
decrement={decrementBreakClick}
increment={incrementBreakClick}
/>
<SessionLength
decrement={decrementSessionClick}
increment={incrementSessionClick}
sessionLength={sessionLength}
/>
</div>
<div id="timer-container">
<div id="timer-div">
<h2 id="timer-label">{timerType}</h2>
{/*Calling a function so need to add () */}
<span id="time-left">{timerToClock()}</span>
</div>
</div>
<div id="timer-controls-container">
<button id="start_stop" onClick={startStopClick}>
<i className="fa fa-play"></i>
<i className="fa fa-pause"></i>
</button>
<button id="reset" onClick={resetClick}>
<i className="fa fa-sync"></i>
</button>
<audio
id="beep"
src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav"
></audio>
</div>
<div id="credit-container">
Designed and coded by:
<br />
<a
href="https://codepen.io/your-work/"
target="_blank"
rel="noreferrer"
>
Hunter Lacefield
</a>
</div>
</div>
</div>
);
}
function BreakLength(props) {
return (
<div id="break-length-container">
<h3 id="break-label">Break Length</h3>
<button
id="break-decrement"
className="down-button"
onClick={props.decrement}
>
<i className="fa fa-arrow-down"></i>
</button>
<span id="break-length" className="break-session-number">
{props.breakLength}
</span>
<button
id="break-increment"
className="up-button"
onClick={props.increment}
>
<i className="fa fa-arrow-up"></i>
</button>
</div>
);
}
function SessionLength(props) {
return (
<div id="session-length-container">
<h3 id="session-label">Session Length</h3>
<button
id="session-decrement"
className="down-button"
onClick={props.decrement}
>
<i className="fa fa-arrow-down"></i>
</button>
<span id="session-length" className="break-session-number">
{props.sessionLength}
</span>
<button
id="session-increment"
className="up-button"
onClick={props.increment}
>
<i className="fa fa-arrow-up"></i>
</button>
</div>
);
}
ReactDOM.render(<Timer />, document.getElementById("root"));
I think the problem is because you clearTimeout as soon you initiate it.
React.useEffect(() => {
let timeoutId;
// If timer is > 0 and the timerState is clicked to running
if (timer > 0 && timerState === "running") {
timeoutId= setTimeout(() => setTimer(timer - 1), 1000);
// Must use setTimeout as it only triggers expression once. setInterval will continuously call expression until told otherwise.
}
// If session timer ends.
else if (timer === 0 && timerType === "session") {
setTimerType("break"); // Change timer type back to break
setTimer(breakLength * 60); // Multiply by 60 because we need to convert minutes into seconds
playSound();
}
// If break timer ends
else if (timer === 0 && timerType === "break") {
setTimerType("session"); // Change timer type break
setTimer(sessionLength * 60); // Multiply by 60 because we need to convert minutes into seconds
playSound();
}
// This must be the clean-up function
() => {
if(timeoutId)
clearTimeout(timeoutId);
}
}, [....]
and if your are using timeoutId anywhere else, you could additionally set it to the state. If else, don't need a state for it.
Hope that works!

How to use conditional to generate element on the page

for (var k = 0; k < 10; k++) {
if (k % 2 === 0) {
weatherText = <div className="in_break">
}
weatherText += <div className="eachD" key={k}>
<div>
{
countIt === 0 ? (currDate.getHours() > 12 ? "Tonight" : "Today") : dayOfWeek[weekDay]
}
</div>
<div>
{
getDate
}
</div>
<div>
{
<ReturnIcon />
}
</div>
</div>
if (k % 2 === 0) {
weatherText += </div>
}
}
What I am looking to do is group all the eachD by two inside the `in_break' div
But I keep getting:
Parsing error: Unexpected token 'weatherText = </div>'
This is the layout:
in_break
eachD
eachD
in_break
eachD
eachD
in_break
eachD
eachD
...
Please help me resolve my issue
UPDATED
I hope this find it's way to your demand:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
renderInBreak = () => {
const items = this.setWeatherTextItems();
const inBreakItems = [];
let breakBlock = [];
let newBreak = false;
items.forEach((textItem, index) => { //1
if(!newBreak) {
breakBlock.push(textItem);
if(index + 1 === items.length){
inBreakItems.push(breakBlock);
}
} else {
inBreakItems.push(breakBlock);
breakBlock = [];
breakBlock.push(textItem);
//without this condition check, the last element will be left out of an odd array length
if(index + 1 === items.length) {
inBreakItems.push(breakBlock)
}
}
if(index % 2) newBreak = true; //false
else newBreak = false; //false
});
return inBreakItems.map(twoTextWeatherItems => (
<div className="in_break">
{twoTextWeatherItems}
</div>
))
}
render(){
<div>
{this.renderInBreak()}
</div>
}
OLD
React is supposed to handle things differently, maybe this will work:
Define a method in your component that will set your items:
setWeatherTextItems = (countId, currDate, dayOfWeek, weekDay, getDate) => {
// you make sure all the variables such like countId and currDate are available inside this function.
const items = [];
for (var k = 0; k < 10; k++) {
items.push(
<div className="eachD" key={k}>
<div>
{countIt === 0
? currDate.getHours() > 12
? "Tonight"
: "Today"
: dayOfWeek[weekDay]}
</div>
<div>{getDate}</div>
<div>{<ReturnIcon />}</div>
</div>
);
}
return items;
}
in your render method, or where you are willing to render these items:
render(){
<div className="in_break">{this.setWeatherTextItems()}</div>
}
Read more about how to render things in a loop.
You can add the conditions you want inside the for loop, or where it makes sense to you.
Not sure if the logic would work in a react environment but as far as I can see from your plain code when you are going to add the 'in_break' div aren't you just assigning the whole whetherText again instead of joining text to it?
Shouldn't this:
if (k % 2 === 0) {
weatherText = </div>
}
be written like this?
if (k % 2 === 0) {
weatherText += </div>
}
Edit following the typo correction:
I tried to run your code on codepen to have a quicker and easier understanding on how to find a solution.
I created an helper function with your code then I returned
<div className="Container" dangerouslySetInnerHTML={{__html: weatherText}}></div>
This enables you to have the result you are looking for. Only the even elements have the 'in_break' class.
Hope this helped and let me know if this is not correct.
Codepen: https://codepen.io/dpgian/pen/EBzRmX

angularjs <timer /> won't update

I have this angularjs controller
angular.module('dashboardApp', ['timer']);
function timingController($scope) {
$scope.timerRunning = false;
$scope.startTimer = function () {
$scope.$broadcast('timer-start');
$scope.timerRunning = true;
};
$scope.stopTimer = function () {
$scope.$broadcast('timer-stop');
$scope.timerRunning = false;
};
$scope.resumeTimer = function () {
$scope.$broadcast('timer-resume');
$scope.timerRunning = true;
};
}
timingController.$inject = ['$scope'];
and this HTML
<div class="row" ng-controller="timingController">
<div class="col-md-12">
<p>
<timer> minutes, seconds.</timer><br />
<button type="button" ng-click="startTimer()" ng-show="!timerRunning" ng-disabled="timerRunning" class="btn btn-success btn-lg btn-block" id="start-timer">Start Timer</button>
<button type="button" ng-click="resumeTimer()" ng-show="timerRunning" ng-disabled="!timerRunning" class="btn btn-success btn-lg btn-block" id="resume-timer">Resume Timer</button>
<button type="button" ng-click="stopTimer()" ng-show="timerRunning" ng-disabled="!timerRunning" class="btn btn-danger btn-lg btn-block" id="stop-timer">Stop Timer</button>
<button type="button" class="btn btn-danger btn-lg btn-block" id="reset-timer" style="display:none;">Reset Timer</button>
</p>
</div>
</div>
The controller is detected, methods are called and i have inserted the timer js directive.. But The values doen't seem to update (broadcast doesn't seem to work). Do you see any problem with this code?
I haven't got any online hosting to host the timer.js script, so my jsfiddle here has problems loading :(
Just change <timer> minutes, seconds.</timer> to <timer>{{minutes}} minutes, {{seconds}} seconds.</timer>.
See the code snippet below. (I embedded the code for the timer directive only so that you can test it here.)
angular.module('dashboardApp', ['timer']);
function timingController($scope) {
$scope.timerRunning = false;
$scope.startTimer = function () {
$scope.$broadcast('timer-start');
$scope.timerRunning = true;
};
$scope.stopTimer = function () {
$scope.$broadcast('timer-stop');
$scope.timerRunning = false;
};
$scope.resumeTimer = function () {
$scope.$broadcast('timer-resume');
$scope.timerRunning = true;
};
}
timingController.$inject = ['$scope'];
/**
* angular-timer - v1.1.6 - 2014-07-01 7:37 AM
* https://github.com/siddii/angular-timer
*
* Copyright (c) 2014 Siddique Hameed
* Licensed MIT <https://github.com/siddii/angular-timer/blob/master/LICENSE.txt>
*/
var timerModule = angular.module('timer', [])
.directive('timer', ['$compile', function ($compile) {
return {
restrict: 'EAC',
replace: false,
scope: {
interval: '=interval',
startTimeAttr: '=startTime',
endTimeAttr: '=endTime',
countdownattr: '=countdown',
finishCallback: '&finishCallback',
autoStart: '&autoStart',
maxTimeUnit: '='
},
controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
// Checking for trim function since IE8 doesn't have it
// If not a function, create tirm with RegEx to mimic native trim
if (typeof String.prototype.trim !== 'function') {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
//angular 1.2 doesn't support attributes ending in "-start", so we're
//supporting both "autostart" and "auto-start" as a solution for
//backward and forward compatibility.
$scope.autoStart = $attrs.autoStart || $attrs.autostart;
if ($element.html().trim().length === 0) {
$element.append($compile('<span>{{millis}}</span>')($scope));
} else {
$element.append($compile($element.contents())($scope));
}
$scope.startTime = null;
$scope.endTime = null;
$scope.timeoutId = null;
$scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) >= 0 ? parseInt($scope.countdownattr, 10) : undefined;
$scope.isRunning = false;
$scope.$on('timer-start', function () {
$scope.start();
});
$scope.$on('timer-resume', function () {
$scope.resume();
});
$scope.$on('timer-stop', function () {
$scope.stop();
});
$scope.$on('timer-clear', function () {
$scope.clear();
});
$scope.$on('timer-set-countdown', function (e, countdown) {
$scope.countdown = countdown;
});
function resetTimeout() {
if ($scope.timeoutId) {
clearTimeout($scope.timeoutId);
}
}
$scope.start = $element[0].start = function () {
$scope.startTime = $scope.startTimeAttr ? new Date($scope.startTimeAttr) : new Date();
$scope.endTime = $scope.endTimeAttr ? new Date($scope.endTimeAttr) : null;
if (!$scope.countdown) {
$scope.countdown = $scope.countdownattr && parseInt($scope.countdownattr, 10) > 0 ? parseInt($scope.countdownattr, 10) : undefined;
}
resetTimeout();
tick();
$scope.isRunning = true;
};
$scope.resume = $element[0].resume = function () {
resetTimeout();
if ($scope.countdownattr) {
$scope.countdown += 1;
}
$scope.startTime = new Date() - ($scope.stoppedTime - $scope.startTime);
tick();
$scope.isRunning = true;
};
$scope.stop = $scope.pause = $element[0].stop = $element[0].pause = function () {
var timeoutId = $scope.timeoutId;
$scope.clear();
$scope.$emit('timer-stopped', {timeoutId: timeoutId, millis: $scope.millis, seconds: $scope.seconds, minutes: $scope.minutes, hours: $scope.hours, days: $scope.days});
};
$scope.clear = $element[0].clear = function () {
// same as stop but without the event being triggered
$scope.stoppedTime = new Date();
resetTimeout();
$scope.timeoutId = null;
$scope.isRunning = false;
};
$element.bind('$destroy', function () {
resetTimeout();
$scope.isRunning = false;
});
function calculateTimeUnits() {
// compute time values based on maxTimeUnit specification
if (!$scope.maxTimeUnit || $scope.maxTimeUnit === 'day') {
$scope.seconds = Math.floor(($scope.millis / 1000) % 60);
$scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
$scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
$scope.days = Math.floor((($scope.millis / (3600000)) / 24));
$scope.months = 0;
$scope.years = 0;
} else if ($scope.maxTimeUnit === 'second') {
$scope.seconds = Math.floor($scope.millis / 1000);
$scope.minutes = 0;
$scope.hours = 0;
$scope.days = 0;
$scope.months = 0;
$scope.years = 0;
} else if ($scope.maxTimeUnit === 'minute') {
$scope.seconds = Math.floor(($scope.millis / 1000) % 60);
$scope.minutes = Math.floor($scope.millis / 60000);
$scope.hours = 0;
$scope.days = 0;
$scope.months = 0;
$scope.years = 0;
} else if ($scope.maxTimeUnit === 'hour') {
$scope.seconds = Math.floor(($scope.millis / 1000) % 60);
$scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
$scope.hours = Math.floor($scope.millis / 3600000);
$scope.days = 0;
$scope.months = 0;
$scope.years = 0;
} else if ($scope.maxTimeUnit === 'month') {
$scope.seconds = Math.floor(($scope.millis / 1000) % 60);
$scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
$scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
$scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30);
$scope.months = Math.floor((($scope.millis / (3600000)) / 24) / 30);
$scope.years = 0;
} else if ($scope.maxTimeUnit === 'year') {
$scope.seconds = Math.floor(($scope.millis / 1000) % 60);
$scope.minutes = Math.floor((($scope.millis / (60000)) % 60));
$scope.hours = Math.floor((($scope.millis / (3600000)) % 24));
$scope.days = Math.floor((($scope.millis / (3600000)) / 24) % 30);
$scope.months = Math.floor((($scope.millis / (3600000)) / 24 / 30) % 12);
$scope.years = Math.floor(($scope.millis / (3600000)) / 24 / 365);
}
// plural - singular unit decision
$scope.secondsS = $scope.seconds == 1 ? '' : 's';
$scope.minutesS = $scope.minutes == 1 ? '' : 's';
$scope.hoursS = $scope.hours == 1 ? '' : 's';
$scope.daysS = $scope.days == 1 ? '' : 's';
$scope.monthsS = $scope.months == 1 ? '' : 's';
$scope.yearsS = $scope.years == 1 ? '' : 's';
//add leading zero if number is smaller than 10
$scope.sseconds = $scope.seconds < 10 ? '0' + $scope.seconds : $scope.seconds;
$scope.mminutes = $scope.minutes < 10 ? '0' + $scope.minutes : $scope.minutes;
$scope.hhours = $scope.hours < 10 ? '0' + $scope.hours : $scope.hours;
$scope.ddays = $scope.days < 10 ? '0' + $scope.days : $scope.days;
$scope.mmonths = $scope.months < 10 ? '0' + $scope.months : $scope.months;
$scope.yyears = $scope.years < 10 ? '0' + $scope.years : $scope.years;
}
//determine initial values of time units and add AddSeconds functionality
if ($scope.countdownattr) {
$scope.millis = $scope.countdownattr * 1000;
$scope.addCDSeconds = $element[0].addCDSeconds = function (extraSeconds) {
$scope.countdown += extraSeconds;
$scope.$digest();
if (!$scope.isRunning) {
$scope.start();
}
};
$scope.$on('timer-add-cd-seconds', function (e, extraSeconds) {
$timeout(function () {
$scope.addCDSeconds(extraSeconds);
});
});
$scope.$on('timer-set-countdown-seconds', function (e, countdownSeconds) {
if (!$scope.isRunning) {
$scope.clear();
}
$scope.countdown = countdownSeconds;
$scope.millis = countdownSeconds * 1000;
calculateTimeUnits();
});
} else {
$scope.millis = 0;
}
calculateTimeUnits();
var tick = function () {
$scope.millis = new Date() - $scope.startTime;
var adjustment = $scope.millis % 1000;
if ($scope.endTimeAttr) {
$scope.millis = $scope.endTime - new Date();
adjustment = $scope.interval - $scope.millis % 1000;
}
if ($scope.countdownattr) {
$scope.millis = $scope.countdown * 1000;
}
if ($scope.millis < 0) {
$scope.stop();
$scope.millis = 0;
calculateTimeUnits();
if($scope.finishCallback) {
$scope.$eval($scope.finishCallback);
}
return;
}
calculateTimeUnits();
//We are not using $timeout for a reason. Please read here - https://github.com/siddii/angular-timer/pull/5
$scope.timeoutId = setTimeout(function () {
tick();
$scope.$digest();
}, $scope.interval - adjustment);
$scope.$emit('timer-tick', {timeoutId: $scope.timeoutId, millis: $scope.millis});
if ($scope.countdown > 0) {
$scope.countdown--;
}
else if ($scope.countdown <= 0) {
$scope.stop();
if($scope.finishCallback) {
$scope.$eval($scope.finishCallback);
}
}
};
if ($scope.autoStart === undefined || $scope.autoStart === true) {
$scope.start();
}
}]
};
}]);
/* commonjs package manager support (eg componentjs) */
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
module.exports = timerModule;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="row" ng-app="dashboardApp">
<div class="col-md-12"><div class="row" ng-controller="timingController">
<div class="col-md-12">
<p>
<h3>Timer 1: <timer autostart="false" /></h3>
<h3>Timer 2: <timer autostart="false" interval="2000"/></h3>
<h3>Timer 3: <timer autostart="false">{{minutes}} minutes, {{seconds}} seconds.</timer></h3>
<button type="button" ng-click="startTimer()" ng-show="!timerRunning" ng-disabled="timerRunning" class="btn btn-success btn-lg btn-block" id="start-timer">Start Timer</button>
<button type="button" ng-click="resumeTimer()" ng-show="!timerRunning" ng-disabled="timerRunning" class="btn btn-success btn-lg btn-block" id="resume-timer">Resume Timer</button>
<button type="button" ng-click="stopTimer()" ng-show="timerRunning" ng-disabled="!timerRunning" class="btn btn-danger btn-lg btn-block" id="stop-timer">Stop Timer</button>
<button type="button" class="btn btn-danger btn-lg btn-block" id="reset-timer" style="display:none;">Reset Timer</button>
</p>
</div>
</div>
</div>
</div>

Resources