Created
August 25, 2014 11:49
-
-
Save bennadel/9df09b930516e5f37127 to your computer and use it in GitHub Desktop.
Creating A Reusable Timer In AngularJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script type="text/javascript"> | |
function prepareToDoSomething() { | |
if ( timer ) { | |
clearTimeout( timer ); | |
} | |
timer = setTimeout( doSomething, duration ); | |
} | |
var timer = null; | |
var duration = 2000; | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html ng-app="Demo"> | |
<head> | |
<meta charset="utf-8" /> | |
<title> | |
Creating A Reusable Timer In AngularJS | |
</title> | |
<link rel="stylesheet" type="text/css" href="./demo.css"></link> | |
</head> | |
<body ng-controller="AppController"> | |
<h1> | |
Creating A Reusable Timer In AngularJS | |
</h1> | |
<p> | |
<a ng-click="handleClick()">Click me</a> to star the timer! | |
</p> | |
<p ng-if="logExecutedAt"> | |
Executed: {{ logExecutedAt.getTime() }} | |
</p> | |
<!-- Load scripts. --> | |
<script type="text/javascript" src="../../vendor/jquery/jquery-2.1.0.min.js"></script> | |
<script type="text/javascript" src="../../vendor/angularjs/angular-1.2.22.min.js"></script> | |
<script type="text/javascript"> | |
// Create an application module for our demo. | |
var app = angular.module( "Demo", [] ); | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// I control the root of the application. | |
app.controller( | |
"AppController", | |
function( $scope, timer ) { | |
// I am a timer that will invoke the given callback once the timer has | |
// finished. The timer can be reset at any time. | |
// -- | |
// NOTE: Is a thin wrapper around $timeout() which will trigger a $digest | |
// when the callback is invoked. | |
var logClickTimer = timer( logClick, timer.TWO_SECONDS ); | |
$scope.logExecutedAt = null; | |
// When the current scope is destroyed, we want to make sure to stop | |
// the current timer (if it's still running). And, give it a chance to | |
// clean up its own internal memory structures. | |
$scope.$on( | |
"$destroy", | |
function() { | |
logClickTimer.teardown(); | |
} | |
); | |
// --- | |
// PUBLIC METHODS. | |
// --- | |
// I handle the click event. Instead of logging the click right away, | |
// we're going to throttle the click through a timer. | |
$scope.handleClick = function() { | |
$scope.logExecutedAt = null; | |
logClickTimer.restart(); | |
}; | |
// --- | |
// PRIVATE METHODS. | |
// --- | |
// I log the fact that the click happened at this point in time. | |
function logClick() { | |
$scope.logExecutedAt = new Date(); | |
} | |
} | |
); | |
// -------------------------------------------------- // | |
// -------------------------------------------------- // | |
// I create timers that wrap the $timeout and provide easy ways to cancel and | |
// reset the timer. | |
app.factory( | |
"timer", | |
function( $timeout ) { | |
// I provide a simple wrapper around the core $timeout that allows for | |
// the timer to be easily reset. | |
function Timer( callback, duration, invokeApply ) { | |
// Store properties. | |
this._callback = callback; | |
this._duration = ( duration || 0 ); | |
this._invokeApply = ( invokeApply !== false ); | |
// I hold the $timeout promise. This will only be non-null when the | |
// timer is actively counting down to callback invocation. | |
this._timer = null; | |
} | |
// Define the instance methods. | |
Timer.prototype = { | |
// Set constructor to help with instanceof operations. | |
constructor: Timer, | |
// I determine if the timer is currently counting down. | |
isActive: function() { | |
return( !! this._timer ); | |
}, | |
// I stop (if it is running) and then start the timer again. | |
restart: function() { | |
this.stop(); | |
this.start(); | |
}, | |
// I start the timer, which will invoke the callback upon timeout. | |
start: function() { | |
var self = this; | |
// NOTE: Instead of passing the callback directly to the timeout, | |
// we're going to wrap it in an anonymous function so we can set | |
// the enable flag. We need to do this approach, rather than | |
// binding to the .then() event since the .then() will initiate a | |
// digest, which the user may not want. | |
this._timer = $timeout( | |
function handleTimeoutResolve() { | |
try { | |
self._callback.call( null ); | |
} finally { | |
self = self._timer = null; | |
} | |
}, | |
this._duration, | |
this._invokeApply | |
); | |
}, | |
// I stop the current timer, if it is running, which will prevent the | |
// callback from being invoked. | |
stop: function() { | |
$timeout.cancel( this._timer ); | |
this._timer = false; | |
}, | |
// I clean up the internal object references to help garbage | |
// collection (hopefully). | |
teardown: function() { | |
this.stop(); | |
this._callback = null; | |
this._duration = null; | |
this._invokeApply = null; | |
this._timer = null; | |
} | |
}; | |
// Create a factory that will call the constructor. This will simplify | |
// the calling context. | |
function timerFactory( callback, duration, invokeApply ) { | |
return( new Timer( callback, duration, invokeApply ) ); | |
} | |
// Store the actual constructor as a factory property so that it is still | |
// accessible if anyone wants to use it directly. | |
timerFactory.Timer = Timer; | |
// Set up some time-based constants to help readability of code. | |
timerFactory.ONE_SECOND = ( 1 * 1000 ); | |
timerFactory.TWO_SECONDS = ( 2 * 1000 ); | |
timerFactory.THREE_SECONDS = ( 3 * 1000 ); | |
timerFactory.FOUR_SECONDS = ( 4 * 1000 ); | |
timerFactory.FIVE_SECONDS = ( 5 * 1000 ); | |
// Return the factory. | |
return( timerFactory ); | |
} | |
); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Excellent wrapper... how to write jasmine unit test case..