Interference between a countdown directive and an angular controller? - javascript

I have some extremely weird interference between two angular functions in my application that I cannot explain. The end result of the problem causes this to happen:
I have a countdown directive embedded in one of my pages which is also the domain of an angular controller. Here is the countdown directive:
(function() {
var app = angular.module('app');
app.directive('countdown', ['$interval', function($interval) {
return {
restrict: 'E',
scope: {
specificity: '=',
countdownTo: '=',
callback: '&?'
},
link: function($scope, elem, attrs) {
$scope.isLaunchExact = ($scope.specificity == 6 || $scope.specificity == 7);
$scope.$watch('specificity', function(newValue) {
$scope.isLaunchExact = (newValue == 6 || newValue == 7);
});
var countdownProcessor = function() {
var launchUnixSeconds = $scope.launchUnixSeconds;
var currentUnixSeconds = Math.floor(Date.now() / 1000);
if (launchUnixSeconds >= currentUnixSeconds) {
$scope.secondsAwayFromLaunch = launchUnixSeconds - currentUnixSeconds;
var secondsBetween = $scope.secondsAwayFromLaunch;
// Calculate the number of days, hours, minutes, seconds
$scope.days = Math.floor(secondsBetween / (60 * 60 * 24));
secondsBetween -= $scope.days * 60 * 60 * 24;
$scope.hours = Math.floor(secondsBetween / (60 * 60));
secondsBetween -= $scope.hours * 60 * 60;
$scope.minutes = Math.floor(secondsBetween / 60);
secondsBetween -= $scope.minutes * 60;
$scope.seconds = secondsBetween;
$scope.daysText = $scope.days == 1 ? 'Day' : 'Days';
$scope.hoursText = $scope.hours == 1 ? 'Hour' : 'Hours';
$scope.minutesText = $scope.minutes == 1 ? 'Minute' : 'Minutes';
$scope.secondsText = $scope.seconds == 1 ? 'Second' : 'Seconds';
} else {
}
if (attrs.callback) {
$scope.callback();
}
};
// Countdown here
if ($scope.isLaunchExact) {
$scope.launchUnixSeconds = moment($scope.countdownTo).unix();
$interval(countdownProcessor, 1000);
} else {
$scope.countdownText = $scope.countdownTo;
}
},
templateUrl: '/js/templates/countdown.html'
}
}]);
})();
Here is the Angular controller that is being interfered with:
(function() {
var app = angular.module('app', []);
//app.value('duScrollDuration', 1000);
app.controller("homeController", ['$scope', 'Statistic', function($scope, Statistic) {
$scope.statistics = [];
$scope.activeStatistic = false;
$scope.goToClickedStatistic = function(statisticType) {
history.replaceState('', document.title, '#' + statisticType);
$scope.activeStatistic = statisticType;
};
$scope.goToNeighborStatistic = function(index) {
if (index >= 0 && index < $scope.statistics.length) {
var stat = $scope.statistics[index];
history.replaceState('', document.title, '#' + stat.camelCaseType); // WhyIsThisFlashing
$scope.activeStatistic = stat.camelCaseType;
return stat.camelCaseType;
} else {
$scope.goHome();
}
};
$scope.goToFirstStatistic = function() {
};
$scope.goHome = function() {
history.replaceState('', document.title, window.location.pathname);
$scope.activeStatistic = false;
return 'home';
};
/*$window.on('scroll',
$.debounce(100, function() {
$('div[data-stat]').fracs('max', 'visible', function(best) {
$scope.activeStatistic($(best).data('stat'));
});
})
);*/
(function() {
laravel.statistics.forEach(function(statistic) {
$scope.statistics.push(new Statistic(statistic));
});
if (window.location.hash) {
$scope.activeStatistic = window.location.hash.substring(1);
}
})();
}]);
app.factory('Statistic', function() {
return function(statistic) {
var self = {};
self.changeSubstatistic = function(newSubstatistic) {
self.activeSubstatistic = newSubstatistic;
};
statistic.forEach(function(substatistic) {
if (!self.substatistics) {
self.substatistics = [];
self.activeSubstatistic = substatistic;
self.type = substatistic.type;
self.camelCaseType = self.type.replace(" ", "");
}
self.substatistics.push(substatistic);
});
return self;
}
});
})();
Each time my countdown directive's countdownProcessor function present in the $interval is run, goToNeighborStatistic in my angular controller is, somehow, called. I don't have a clue why.
If I step through my countdown directive, after stepping through the countdownProcessor function, an "Anonymous Script" named SCRIPT0 is called with the following contents:
"use strict";
var fn=function(s,l,a,i){var v0,v1,v2,v3=l&&('goToNeighborStatistic' in l),v4,v5,v6=l&&('\u0024index' in l);v2=v3?l:s;if(!(v3)){if(s){v1=s.goToNeighborStatistic;}}else{v1=l.goToNeighborStatistic;}if(v1!=null){ensureSafeFunction(v1,text);if(!(v6)){if(s){v5=s.$index;}}else{v5=l.$index;}v4=ifDefined(v5,0)-ifDefined(1,0);ensureSafeObject(v2,text);v0=ensureSafeObject(v2.goToNeighborStatistic(ensureSafeObject(ifDefined(v5,0)-ifDefined(1,0),text)),text);}else{v0=undefined;}return v0;};return fn;
Why is "goToNeighborStatistic" present in this? Why is this script even present? From there, goToNeighborStatistic() runs, at which point, the countdown is called again and everything repeats. What's going on here?
EDIT:
In Chrome, stepping through everything produces different results. At the end of the countdownProcessor function, stepping into produces this:

Related

Binding child element value from Angular directive

Binding values with $scope.minutes = 1 to ng-bind="minutes" not working when I add scope: {finishcallback: "&"}, to my directive.
I'm trying to implement a countdown timer with Angular directives but cannot set the value of the remaining minute and second to the child span element when I define a scope function in the directive.
<time id="countdown_{{order.Id}}" ng-if="order.StatusCode == 1" countdown="{{order.RemainingTimeToPrepareOrder}}" finishcallback="vm.countdownfinished(parameter)" callbackparameter="{{order.Id}}" countdownfinished="toggle()">
<b> <span class="value" ng-bind="minutes"></span> dakika <span class="value" ng-bind="seconds">--</span> saniye</b>
</time>
And here is my directive code.
function countdown() {
return {
restrict: 'A',
scope: {
finishcallback: "&"
},
controller: function ($scope, $attrs, $timeout) {
$attrs.$observe('countdown', function (value) {
var ds = new Date();
ds.setTime(value * 1000);
$scope.days = '-';
$scope.hours = $scope.minutes = $scope.seconds = '--';
$scope.timeout = $timeout(update, 1000);
function update() {
now = +new Date();
$scope.delta = Math.round((ds - now) / 1000);
if ($scope.delta >= 0) {
$timeout(update, 1000);
} else if ($attrs.countdownfinished) {
$scope.$apply($attrs.countdownfinished);
}
}
});
},
link: function ($scope, $element, $attrs) {
$scope.$watch('delta', function (delta) {
if (typeof delta === 'undefined') return;
if (delta < 0) {
delta = 0;
}
$scope.days = Math.floor(delta / 86400);
$scope.hours = forceTwoDigits(Math.floor(delta / 3600) % 24);
$scope.minutes = forceTwoDigits(Math.floor(delta / 60) % 60);
$scope.seconds = forceTwoDigits(delta % 60);
});
$scope.toggle = function () {
$scope.finishcallback({ parameter: $attrs.callbackparameter });
}
function forceTwoDigits(num) {
return String(num < 10 ? '0' + num : num);
}
}
}
}
All the functionality is working until I add finishcallback: "&" scope variable in my directive. I added this to enable custom function calls when the countdown finished. But when I add this my assignments like $scope.minutes stopped to change values in my spans.
How do I change span values dynamically even I define a scope in my directive ?
I'd recommend to just use a template:
function countdown($timeout) {
return {
restrict: 'A',
scope: {
finishcallback: "&"
},
template: `<b> <span class="value" ng-bind="minutes"></span> dakika <span class="value" ng-bind="seconds">--</span> saniye</b>`,
controller: function($scope, $attrs) {
$attrs.$observe('countdown', function(value) {
var ds = new Date();
ds.setTime(value * 1000);
$scope.days = '-';
$scope.hours = $scope.minutes = $scope.seconds = '--';
$scope.timeout = $timeout(update, 1000);
function update() {
now = +new Date();
$scope.delta = Math.round((ds - now) / 1000);
if ($scope.delta >= 0) {
$timeout(update, 1000);
} else if ($attrs.countdownfinished) {
$scope.$apply($attrs.countdownfinished);
}
}
});
},
link: function($scope, $element, $attrs) {
$scope.$watch('delta', function(delta) {
if (typeof delta === 'undefined') return;
if (delta < 0) {
delta = 0;
}
$scope.days = Math.floor(delta / 86400);
$scope.hours = forceTwoDigits(Math.floor(delta / 3600) % 24);
$scope.minutes = forceTwoDigits(Math.floor(delta / 60) % 60);
$scope.seconds = forceTwoDigits(delta % 60);
});
$scope.toggle = function() {
$scope.finishcallback({
parameter: $attrs.callbackparameter
});
}
function forceTwoDigits(num) {
return String(num < 10 ? '0' + num : num);
}
}
}
}
angular.module('app', [])
.controller('ctrl', function($scope, $interval) {
$scope.order = {
Id: 1,
StatusCode: 1,
RemainingTimeToPrepareOrder: Date.now() + 5 * 60 * 1000,
};
$scope.countdownfinished = function(parameter) {
console.log(parameter);
}
$scope.toggle = function() {
console.log("Toggle");
}
$interval(function() {
$scope.order.RemainingTimeToPrepareOrder -= 1000;
}, 1000);
})
.directive('countdown', countdown);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.js"></script>
<div ng-app="app" ng-controller="ctrl">
<time id="countdown_{{order.Id}}" ng-if="order.StatusCode == 1" countdown="{{order.RemainingTimeToPrepareOrder}}" finishcallback="countdownfinished(parameter)" callbackparameter="{{order.Id}}" countdownfinished="toggle()">
</time>
</div>

Javascript not able to make common functions for a prototype

I am trying to make timer in javascript using a prototype. Each time a new timer is created, a object of prototype is created. There are methods to increase time and print each second. The whole code snippet is as follows:
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(update, 1000);
}
function update() {
incrementTime();
render();
}
function render() {
self.elem.innerText = getPrintableTime();
}
function incrementTime() {
self.currentTime["min"] += Math.floor((++self.currentTime["sec"]) / 60);
self.currentTime["hr"] += Math.floor(self.currentTime["min"] / 60);
self.currentTime["sec"] = self.currentTime["sec"] % 60;
self.currentTime["min"] = self.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
};
module.exports = Timer;
I have all methods in start function. The problem is that for each new object of Timer, new space for each method will be used which is very inefficient. But when I try to put methods outside of start function, they lose access to self variable. You can see that there is setInterval function used which will be calling these methods per second. I cannot use this also as this will be instance of Window in subsequent calls.
How can I solve this situation by only keeping one instance of all the interior methods?
You don't need to have all methods in the start function. Yes, for each new Timer instance, new space for each function will be used, but that is necessary when you want to work with setInterval as you need a function which closes over the instance. However, you need only one such closure, the other methods can be standard prototype methods.
function getTwoDigitNumber(number) {
return (number > 9 ? "" : "0") + number;
}
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.prototype.start = function() {
var self = this;
if (!this.interval) {
this.interval = setInterval(function update() {
self.incrementTime();
self.render();
}, 1000);
}
};
Timer.prototype.render() {
this.elem.innerText = this.getPrintableTime();
};
Timer.prototype.incrementTime = function() {
this.currentTime.sec += 1;
this.currentTime.min += Math.floor(this.currentTime.sec / 60);
this.currentTime.hr += Math.floor(this.currentTime.min / 60);
this.currentTime.sec = this.currentTime.sec % 60;
this.currentTime.min = this.currentTime.min % 60;
};
Timer.prototype.getPrintableTime = function() {
var text = getTwoDigitNumber(this.currentTime.hr) + ":"
+ getTwoDigitNumber(this.currentTime.min) + ":"
+ getTwoDigitNumber(self.currentTime.sec);
return text;
};
module.exports = Timer;
Btw, regarding your incrementTime pattern, you should have a look at How to create an accurate timer in javascript?.
You can use apply to use functions defined outside of prototype with correct this context.
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
function update() {
incrementTime.apply(this);
render.apply(this);
}
function render() {
this.elem.innerText = getPrintableTime.apply(this);
}
function incrementTime() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
}
function getPrintableTime() {
var text = getTwoDigitNumber(this.currentTime["hr"]) + ":" + getTwoDigitNumber(this.currentTime["min"]) + ":" + getTwoDigitNumber(this.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
Timer.prototype.start = function() {
var self = this;
if (!self.interval) {
self.interval = setInterval(function() {
update.apply(self);
}, 1000);
}
};
document.addEventListener('DOMContentLoaded', function() {
var timer = new Timer(document.getElementById('timer'));
timer.start();
}, false);
<div id="timer"></div>
If I understand correctly, you're wanting to only create one interval.
One possible solution would be to create a static method and variable to manage the setInterval. I would note that while this may be more performance friendly, the timers will always start and run on the same count...not from the moment each timer is created. (See example)
Of course, you could capture the current timestamp and calculate the elapsed time from there. But, that's another thread ;)
function Timer(elem) {
this.interval = null;
this.currentTime = {
sec: 0,
min: 0,
hr: 0
};
this.elem = elem;
};
Timer.subscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) === -1) {
Timer.subscribers.push(timer);
timer.update.call(timer);
}
Timer.checkInterval();
};
Timer.unsubscribe = function(timer) {
Timer.subscribers = Timer.subscribers || [];
if (Timer.subscribers.indexOf(timer) !== -1) {
Timer.subscribers.splice(Timer.subscribers.indexOf(timer), 1);
}
Timer.checkInterval();
};
Timer.checkInterval = function() {
if (!Timer.interval && Timer.subscribers.length > 0) {
Timer.interval = setInterval(function() {
Timer.subscribers.forEach(function(item) {
item.update.call(item);
});
}, 1000);
} else if (Timer.interval && Timer.subscribers.length === 0) {
clearInterval(Timer.interval);
Timer.interval = null;
}
};
Timer.prototype = {
start: function() {
Timer.subscribe(this);
},
stop: function() {
Timer.unsubscribe(this);
},
update: function() {
this.incrementTime();
this.render();
},
incrementTime: function() {
this.currentTime["min"] += Math.floor((++this.currentTime["sec"]) / 60);
this.currentTime["hr"] += Math.floor(this.currentTime["min"] / 60);
this.currentTime["sec"] = this.currentTime["sec"] % 60;
this.currentTime["min"] = this.currentTime["min"] % 60;
},
render: function() {
var self = this;
function getPrintableTime() {
var text = getTwoDigitNumber(self.currentTime["hr"]) + ":" + getTwoDigitNumber(self.currentTime["min"]) + ":" + getTwoDigitNumber(self.currentTime["sec"]);
return text;
}
function getTwoDigitNumber(number) {
if (number > 9) {
return "" + number;
} else {
return "0" + number;
}
}
this.elem.innerText = getPrintableTime();
}
};
/**
*
*/
var timers = document.getElementById('timers');
function addTimer() {
var el = document.createElement('div');
var tmr = document.createElement('span');
var btn = document.createElement('button');
var t = new Timer(tmr);
btn.innerText = 'Stop';
btn.onclick = function() {
t.stop();
};
el.appendChild(tmr);
el.appendChild(btn);
timers.appendChild(el);
t.start();
};
<div id="timers"></div>
<button onclick="addTimer()">Add Timer</button>

Angular directive instances $watch not working

I have two timepicker inputs that has a total that I need to calculate.
The code in my directive works fine but the problem comes when I have more than one of the directives on the page.
I tried setting the watches in my directive's controller or in the link function but the watches are only working on the last instantiated directive.
What am I possibly missing?
Edit: Sorry wrong plunkr
Here's a plunkr: https://plnkr.co/edit/uC38NIbYsy9Vv3S38xHh?p=preview
Directive code:
myApp.directive('myTimepicker', function() {
return {
restrict: 'A',
scope: {
tmodel: '=',
ttitle: '#'
},
link: function(scope, $element, attr) {
console.log(scope);
scope.tform = scope.tmodel;
scope.$watch('tform.on', function(newValue, oldValue) {
// console.log("calc on"+scope.ttitle);
_calctotal();
});
scope.$watch('tform.off', function(newValue, oldValue) {
// console.log("calc off");
_calctotal();
});
_calctotal = function() {
var on = new Date(scope.tform.on);
var off = new Date(scope.tform.off);
var total = off.getHours() - on.getHours();
var totalmin = off.getMinutes() - on.getMinutes();
if (totalmin < 0) {
total = total - 1;
totalmin = totalmin * -1;
}
if (total < 0) {
total = "Invalid";
totalmin = "";
}
if (totalmin < 10) totalmin = "0" + totalmin;
scope.tform.total = total + ":" + totalmin;
};
_calctotal();
},
controller: function($scope) {
// console.log($scope);
},
templateUrl: "mytimepicker.html"
}
});
Try using ng-change instead of $watch, it's cleaner and easier to follow.
Its with your _calc function declaration without a var.
link: function(scope, $element, attr) {
console.log(scope);
scope.tform = scope.tmodel;
var _calctotal = function() {
var on = new Date(scope.tform.on);
var off = new Date(scope.tform.off);
var total = off.getHours() - on.getHours();
var totalmin = off.getMinutes() - on.getMinutes();
if (totalmin < 0) {
total = total - 1;
totalmin = totalmin * -1;
}
if (total < 0) {
total = "Invalid";
totalmin = "";
}
if (totalmin < 10) totalmin = "0" + totalmin;
scope.tform.total = total + ":" + totalmin;
};
scope.$watch('tform.on', function(newValue, oldValue) {
// console.log("calc on"+scope.ttitle);
_calctotal();
});
scope.$watch('tform.off', function(newValue, oldValue) {
// console.log("calc off");
_calctotal();
});
_calctotal();
},
Working sample on Plnkr

AngularJS directive loosing the variable value after filtering

Am having a stopwatch with different titles, when I start a watch it runs and after 10 seconds it pops an alert.I can filter the watch on top of title, when I reset the filter the running watch is not displaying the time but it pops an alert at 10 seconds. Why the binding is not working after filter?What am doing wrong here?How can I fix it?
Find the plunker here Complete code and HTML is in plunker.
Clicking play will start the watch, pause will pause it and stop - stops the watch.
< script type = "text/javascript" > angular.module('App', [])
.controller('MainCtrl', function ($scope, $interval) {
$interval(function () {
$scope.sharedTime = new Date();
}, 500);
})
.directive('stopwatch', function () {
return {
restrict: 'AE',
templateUrl: 'stopwatch.html',
scope: {
// Set title in the isolate scope from the title attribute on the directive's element.
title: '#title',
// Set up a bi-directional binding between currentTime on the local scope and the parent
// scope's variable containing the current time that's the value of the time attribute.
currentTime: '=time'
},
link: function (scope, element, attrs, ctrl) {},
controllerAs: 'swctrl',
controller: function ($scope, $interval) {
var self = this;
var time = 0;
var mode = 1;
var status = 0;
var timer_id;
self.play = 1;
self.start = function (interval) {
self.play = 0;
var timeRet;
interval = 1000;
if (status == 0) {
status = 1;
function timeRun() {
if (time < 86400) {
time++;
timeRet = self.getElapsedMs();
if (time == 10) {
alert("Its 10 seconds")
}
if (typeof (callback) === 'function') callback(timeRet);
}
};
return timer_id = setInterval(timeRun, interval);
}
};
self.pause = function () {
self.play = 1;
if (status == 1) {
status = 0;
clearInterval(timer_id);
}
};
self.stop = function () {
self.play = 1;
sec = (typeof (sec) !== 'undefined') ? sec : 0;
time = sec;
if (status == 1) {
status = 0;
clearInterval(timer_id);
}
};
self.getTime = function () {
return time;
};
self.getElapsedMs = function () {
var second = time % 60;
var minute = Math.floor(time / 60) % 60;
var hour = Math.floor(time / 3600) % 60;
second = (second < 10) ? '0' + second : second;
minute = (minute < 10) ? '0' + minute : minute;
hour = (hour < 10) ? '0' + hour : hour;
var timeObj = hour + ":" + minute + ":" + second
return timeObj;
};
}
}
}); < /script>
How to reproduce: Activate one, then type in "two" in the textbox, then erase it. You'll see that the one timer is showing 00:00:00
Filter in ng-repeat applies your filtering keyword on the array and returns a new subset, thus destroying previously created directives.
From the docs:
Selects a subset of items from array and returns it as a new array.
If you place inside the link function:
scope.$on("$destroy", function () {
console.log("destroying");
});
You'll see that it logs for every stopwatch that is eliminated due to the filtering.
Therefore, I'd just hide the entire div of the stopwatch instead of using the filter:
<div ng-show="!filter || title.indexOf(filter) > -1">
<div class="timer" >{{title}}<br>
{{swctrl.getElapsedMs() | limitTo:6}}<span class="sec">
{{swctrl.getElapsedMs() | limitTo:-2}}</span>
</div>
Play
Pause
<a href="" id="timerStop" class="stop" ng-click="swctrl.stop();" >Stop</a>
</div>
And your usage becomes:
<div ng-repeat="item in arr">
<div stopwatch title="{{item}}" filter="filters" time="sharedTime">
</div>
Plunker
Omri Aharon explained what the issue is. Personally I'd move the stopwatch functionality into a factory and pass instances of this into your directive.
.factory('StopWatch', function() {
function StopWatch(name) {
this.name = name;
this.time = 0;
this.mode = 1;
this.status = 0;
this.play = 1;
}
StopWatch.prototype.start = function(interval) {
var self = this;
self.play = 0;
var timeRet;
interval = 1000;
if(self.status == 0)
{
self.status = 1;
function timeRun()
{
if(self.time < 86400)
{
self.time++;
timeRet = self.getElapsedMs();
if(self.time == 10)
{
alert("Its 10 seconds")
}
if(typeof(callback) === 'function') callback(timeRet);
}
};
return self.timer_id = setInterval(timeRun, interval);
}
};
StopWatch.prototype.pause = function() {
this.play = 1;
if(this.status == 1)
{
this.status = 0;
clearInterval(this.timer_id);
}
};
StopWatch.prototype.stop = function() {
this.play = 1;
var sec = (typeof(sec) !== 'undefined') ? sec : 0;
this.time = sec;
if(this.status == 1)
{
this.status = 0;
clearInterval(this.timer_id);
}
};
StopWatch.prototype.getTime = function() {
return this.time;
};
StopWatch.prototype.getElapsedMs = function() {
var second = this.time % 60;
var minute = Math.floor(this.time / 60) % 60;
var hour = Math.floor(this.time / 3600) % 60;
second = (second < 10) ? '0'+second : second;
minute = (minute < 10) ? '0'+minute : minute;
hour = (hour < 10) ? '0'+hour : hour;
var timeObj = hour+":"+minute+":"+second
return timeObj;
};
return StopWatch;
})
So you'd create the stopwatches:
$scope.timers = [
new StopWatch('one'),
new StopWatch('two'),
new StopWatch('three'),
new StopWatch('four')
];
Then repeat and pass into the directive:
<div ng-repeat="item in timers | filter:filter_">
{{item.name}}
<div stopwatch="item"></div>
</div>
Plunker

JS Countdown timer with days:hours:minutes

I found a neat timer but I need to customize it a bit to work for my project, I tried to change javascript code so it would be a countdown until the weekend and start again from ex: 6days:23hours:59minutes, but I failed miserably
I also would like to know how possible could I add a third row called days
http://codepen.io/anon/pen/ZYEBjP
JS code
var Clock = (function(){
var exports = function(element) {
this._element = element;
var html = '';
for (var i=0;i<6;i++) {
html += '<span> </span>';
}
this._element.innerHTML = html;
this._slots = this._element.getElementsByTagName('span');
this._tick();
};
exports.prototype = {
_tick:function() {
var time = new Date();
this._update(this._pad(time.getHours()) + this._pad(time.getMinutes()) + this._pad(time.getSeconds()));
var self = this;
setTimeout(function(){
self._tick();
},1000);
},
_pad:function(value) {
return ('0' + value).slice(-2);
},
_update:function(timeString) {
var i=0,l=this._slots.length,value,slot,now;
for (;i<l;i++) {
value = timeString.charAt(i);
slot = this._slots[i];
now = slot.dataset.now;
if (!now) {
slot.dataset.now = value;
slot.dataset.old = value;
continue;
}
if (now !== value) {
this._flip(slot,value);
}
}
},
_flip:function(slot,value) {
// setup new state
slot.classList.remove('flip');
slot.dataset.old = slot.dataset.now;
slot.dataset.now = value;
// force dom reflow
slot.offsetLeft;
// start flippin
slot.classList.add('flip');
}
};
return exports;
}());
var i=0,clocks = document.querySelectorAll('.clock'),l=clocks.length;
for (;i<l;i++) {
new Clock(clocks[i]);
}
I would create a helper:
var TimeHelper = function(days, hours, minutes, callback) {
this.subtractMinute = function() {
minutes = (minutes + 60 - 1) % 60;
if (minutes === 0) {
hours = (hours + 60 - 1) % 60;
if (hours === 0) {
days = (days + 24 - 1) % 24;
if (days === 0) {
days = 24;
hours = 0;
minutes = 0;
}
}
}
callback(days, hours, minutes);
}
}
function refreshUI(days, hours, minutes) {
//refresh my ui elements
}
var timeHelper = new TimeHelper(24, 0, 0);
And then you can call timeHelper.subtractMinute once/minute this on every minute

Categories

Resources