1
+ <!doctype html>
2
+ < html ng-app ="Demo ">
3
+ < head >
4
+ < meta charset ="utf-8 " />
5
+
6
+ < title >
7
+ Creating A Reusable Timer In AngularJS
8
+ </ title >
9
+
10
+ < link rel ="stylesheet " type ="text/css " href ="./demo.css "> </ link >
11
+ </ head >
12
+ < body ng-controller ="AppController ">
13
+
14
+ < h1 >
15
+ Creating A Reusable Timer In AngularJS
16
+ </ h1 >
17
+
18
+ < p >
19
+ < a ng-click ="handleClick() "> Click me</ a > to star the timer!
20
+ </ p >
21
+
22
+ < p ng-if ="logExecutedAt ">
23
+ Executed: {{ logExecutedAt.getTime() }}
24
+ </ p >
25
+
26
+
27
+ <!-- Load scripts. -->
28
+ < script type ="text/javascript " src ="../../vendor/jquery/jquery-2.1.0.min.js "> </ script >
29
+ < script type ="text/javascript " src ="../../vendor/angularjs/angular-1.2.22.min.js "> </ script >
30
+ < script type ="text/javascript ">
31
+
32
+ // Create an application module for our demo.
33
+ var app = angular . module ( "Demo" , [ ] ) ;
34
+
35
+
36
+ // -------------------------------------------------- //
37
+ // -------------------------------------------------- //
38
+
39
+
40
+ // I control the root of the application.
41
+ app . controller (
42
+ "AppController" ,
43
+ function ( $scope , timer ) {
44
+
45
+ // I am a timer that will invoke the given callback once the timer has
46
+ // finished. The timer can be reset at any time.
47
+ // --
48
+ // NOTE: Is a thin wrapper around $timeout() which will trigger a $digest
49
+ // when the callback is invoked.
50
+ var logClickTimer = timer ( logClick , timer . TWO_SECONDS ) ;
51
+
52
+ $scope . logExecutedAt = null ;
53
+
54
+ // When the current scope is destroyed, we want to make sure to stop
55
+ // the current timer (if it's still running). And, give it a chance to
56
+ // clean up its own internal memory structures.
57
+ $scope . $on (
58
+ "$destroy" ,
59
+ function ( ) {
60
+
61
+ logClickTimer . teardown ( ) ;
62
+
63
+ }
64
+ ) ;
65
+
66
+
67
+ // ---
68
+ // PUBLIC METHODS.
69
+ // ---
70
+
71
+
72
+ // I handle the click event. Instead of logging the click right away,
73
+ // we're going to throttle the click through a timer.
74
+ $scope . handleClick = function ( ) {
75
+
76
+ $scope . logExecutedAt = null ;
77
+
78
+ logClickTimer . restart ( ) ;
79
+
80
+ } ;
81
+
82
+
83
+ // ---
84
+ // PRIVATE METHODS.
85
+ // ---
86
+
87
+
88
+ // I log the fact that the click happened at this point in time.
89
+ function logClick ( ) {
90
+
91
+ $scope . logExecutedAt = new Date ( ) ;
92
+
93
+ }
94
+
95
+ }
96
+ ) ;
97
+
98
+
99
+ // -------------------------------------------------- //
100
+ // -------------------------------------------------- //
101
+
102
+
103
+ // I create timers that wrap the $timeout and provide easy ways to cancel and
104
+ // reset the timer.
105
+ app . factory (
106
+ "timer" ,
107
+ function ( $timeout ) {
108
+
109
+ // I provide a simple wrapper around the core $timeout that allows for
110
+ // the timer to be easily reset.
111
+ function Timer ( callback , duration , invokeApply ) {
112
+
113
+ // Store properties.
114
+ this . _callback = callback ;
115
+ this . _duration = ( duration || 0 ) ;
116
+ this . _invokeApply = ( invokeApply !== false ) ;
117
+
118
+ // I hold the $timeout promise. This will only be non-null when the
119
+ // timer is actively counting down to callback invocation.
120
+ this . _timer = null ;
121
+
122
+ }
123
+
124
+ // Define the instance methods.
125
+ Timer . prototype = {
126
+
127
+ // Set constructor to help with instanceof operations.
128
+ constructor : Timer ,
129
+
130
+
131
+ // I determine if the timer is currently counting down.
132
+ isActive : function ( ) {
133
+
134
+ return ( ! ! this . _timer ) ;
135
+
136
+ } ,
137
+
138
+
139
+ // I stop (if it is running) and then start the timer again.
140
+ restart : function ( ) {
141
+
142
+ this . stop ( ) ;
143
+ this . start ( ) ;
144
+
145
+ } ,
146
+
147
+
148
+ // I start the timer, which will invoke the callback upon timeout.
149
+ start : function ( ) {
150
+
151
+ var self = this ;
152
+
153
+ // NOTE: Instead of passing the callback directly to the timeout,
154
+ // we're going to wrap it in an anonymous function so we can set
155
+ // the enable flag. We need to do this approach, rather than
156
+ // binding to the .then() event since the .then() will initiate a
157
+ // digest, which the user may not want.
158
+ this . _timer = $timeout (
159
+ function handleTimeoutResolve ( ) {
160
+
161
+ try {
162
+ self . _callback . call ( null ) ;
163
+ } finally {
164
+ self = self . _timer = null ;
165
+ }
166
+
167
+ } ,
168
+ this . _duration ,
169
+ this . _invokeApply
170
+ ) ;
171
+
172
+ } ,
173
+
174
+
175
+ // I stop the current timer, if it is running, which will prevent the
176
+ // callback from being invoked.
177
+ stop : function ( ) {
178
+
179
+ $timeout . cancel ( this . _timer ) ;
180
+
181
+ this . _timer = false ;
182
+
183
+ } ,
184
+
185
+
186
+ // I clean up the internal object references to help garbage
187
+ // collection (hopefully).
188
+ teardown : function ( ) {
189
+
190
+ this . stop ( ) ;
191
+ this . _callback = null ;
192
+ this . _duration = null ;
193
+ this . _invokeApply = null ;
194
+ this . _timer = null ;
195
+
196
+ }
197
+
198
+ } ;
199
+
200
+
201
+ // Create a factory that will call the constructor. This will simplify
202
+ // the calling context.
203
+ function timerFactory ( callback , duration , invokeApply ) {
204
+
205
+ return ( new Timer ( callback , duration , invokeApply ) ) ;
206
+
207
+ }
208
+
209
+ // Store the actual constructor as a factory property so that it is still
210
+ // accessible if anyone wants to use it directly.
211
+ timerFactory . Timer = Timer ;
212
+
213
+ // Set up some time-based constants to help readability of code.
214
+ timerFactory . ONE_SECOND = ( 1 * 1000 ) ;
215
+ timerFactory . TWO_SECONDS = ( 2 * 1000 ) ;
216
+ timerFactory . THREE_SECONDS = ( 3 * 1000 ) ;
217
+ timerFactory . FOUR_SECONDS = ( 4 * 1000 ) ;
218
+ timerFactory . FIVE_SECONDS = ( 5 * 1000 ) ;
219
+
220
+
221
+ // Return the factory.
222
+ return ( timerFactory ) ;
223
+
224
+ }
225
+ ) ;
226
+
227
+ </ script >
228
+
229
+ </ body >
230
+ </ html >
0 commit comments