calling another method inside same view - backbone.js

I making a simple game that uses a two minute JavaScript timer. I can get the javascript timer to work without using backbone. The code is at the bottom for the working timer, and here's a fiddle of it http://jsfiddle.net/mjmitche/hDRjR/19/
However, once I try to the timer code into a few different methods inside a Backbone view, I'm getting an error depending on how a key method, displayTime, code is defined
Steps:
1) I create a new clockView and save it to a variable clock_view
var clock_view = new ClockView({ model: game});
Inside the initializer of clockview, I set up these variables that are used by the timer code
var totalWait = 120;
var secondsRemaining = totalWait;
var hasFocus = true;
var hasJustFailed = false;
The startClock method gets triggered from elsewhere
this.model.bind("gameStartedEvent", this.startClock, this);
startClock uses setInterval to call displayTime method every second. Depending on how displayTime is coded [a) displayTime(), b) this.displayTime(), c) clock_view.displayTime() ], displayTime triggers a different error.
startClock: function(){
console.log("start clock");
setInterval(function(){
this.secondsRemaining -= 1;
console.log("working");
displayTime(); //uncaught reference error: displayTime is not defined
this.displayTime(); //Uncaught TypeError: Object [object Window] has no method 'displayTime'
clock_view.displayTime();// `display time gets called but triggers NAN`
if(secondsRemaining == 0) $('#timer').fadeOut(1000);
}, 1000);
},
If displayTime is called from setInterval as displayTime() it says it's not defined. If I do this.displayTime(), I get a object window has no method. If I call it clock_view.displayTime(), it triggers a NAN error, which I think may be caused because the way the variables are defined in the initializer
displayTime is defined directly below startClock like this
displayTime: function () {
var minutes = Math.floor(secondsRemaining / 60);
var seconds = secondsRemaining - (minutes * 60);
if (seconds < 10) seconds = "0" + seconds;
var time = minutes + ":" + seconds;
$('#timer').html(time);
},
Update
This is a fiddle of the whole ClockView in a Backbone format, although it doesn't work because it's missing other parts of the program (such as the model that triggers the event). I'm including it only to make the question more readable
http://jsfiddle.net/mjmitche/RRXnK/85/
Original working clock code http://jsfiddle.net/mjmitche/hDRjR/19/
var displayTime = function () {
var minutes = Math.floor(secondsRemaining / 60);
var seconds = secondsRemaining - (minutes * 60);
if (seconds < 10) seconds = "0" + seconds;
var time = minutes + ":" + seconds;
$('#timer').html(time);
};
$('#timer').css('marginTop', 0);
setInterval(function(){
secondsRemaining -= 1;
displayTime();
if(secondsRemaining == 0) $('#timer').fadeOut(1000);
}, 1000);

This should work. The main points are, how variables are accessed inside the View.
HTML:
<div id="view">
<div id="timer">
<span class="time">2:00</span>
</div>
<div id="options">
<input type="button" class="action_button" value="New Game" id="new_game">
</div>
</div>
View:
var ClockView = Backbone.View.extend({
el: '#view',
initialize: function () {
/** Define your variables in this View */
this.totalWait = 120;
this.secondsRemaining = this.totalWait;
this.hasFocus = true;
this.hasJustFailed = false;
},
events: {
'click #new_game' : 'startClock' /** The button starts the clock */
},
startClock: function () {
console.log("start clock");
var self = this; /** Save 'this' to a local variable */
setInterval(function () {
self.secondsRemaining -= 1;
self.displayTime();
if (self.secondsRemaining == 0) self.$('#timer').fadeOut(1000);
}, 1000);
},
displayTime: function () {
var minutes = Math.floor(this.secondsRemaining / 60);
var seconds = this.secondsRemaining - (minutes * 60);
if (seconds < 10) seconds = "0" + seconds;
var time = minutes + ":" + seconds;
this.$('#timer').html(time);
},
});
var clock_view = new ClockView();

Related

setInterval in a promise, only executes once in React

I'm trying to create a countdown timer with one second ticking away each second. The interval works only one time and does not execute. The problem is that I do not have this.state.timetill until a server call is made, then I can begin the timer. Therefore, the setInterval function has to be tucked away in a promise, where it executes only one. The timetill variable however can be a constant, once retrieved it does not change.
componentDidMount = async () => {
const that = this;
*do stuff*
this.someFunction().finally(() => {
/*This right here, below is not working */
setInterval(that.createTimer(), 1000);
});
}
My JSX is such
{this.state.setTimer}
function in question, the time has to be divided by 1000 since javascript uses miliseconds
createTimer = async timerNow => {
var timerNow = (await new Date().getTime()) / 1000;
// Find the distance between now and the count down date
var distance = this.state.timeTill - timerNow;
// Time calculations for days, hours, minutes and seconds
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
//the variable displayed
var setTimer = days + "d " + hours + "h " + minutes + "m " + seconds + "s ";
this.setState({setTimer});
};
You pass the returned value of createTimer to setInterval, instead pass the function but don't invoke it
setInterval(this.createTimer, 1000)
There's a few things you should do:
1. Have a method that will actually call setState
2. Change setInterval to call a method reference
3. Cleanup any pending request on unmounting:
4. [optional] you can drop that
private createTimer = { ... }
private setTimer = () => {
this.setState({ timer: this.createTimer() });
}
private _mounted: boolean = false;
private _intervalHandler: number | null = null;
componentDidMount = async () => {
this._mounted = true;
this.someFunction().finally(() => {
if (!this._mounted) { return; }
this._intervalHandler = setInterval(this.setTimer, 1000);
});
}
componentWillUnmoun = () => {
this._mounted = false;
if (this._intervalHandler !== null) {
clearInterval(this._intervalHandler);
}
}
This will ensure you are not performing state changes after the component was unmounted.

How can I set an interval inside the custom filter

I created a custom date filter, but I want to set an interval for every second to make it run like a clock ecvery second. Thank you in advance
This is my code.
app.filter('DateGap', function() {
// In the return function, we must pass in a single parameter which will be the data we will work on.
// We have the ability to support multiple other parameters that can be passed into the filter optionally
return function update(input, optional1, optional2) {
var t1 = new Date(input + 'Z');
var t2 = new Date();
var dif = t1.getTime() - t2.getTime();
var Seconds_from_T1_to_T2 = dif / 1000;
var Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
var sec_num = Math.floor(Seconds_Between_Dates); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (typeof PenddingHours != "undefined")
return hours >= PenddingHours ? true : false;
if (hours < 10) { hours = "0" + hours; }
if (minutes < 10) { minutes = "0" + minutes; }
if (seconds < 10) { seconds = "0" + seconds; }
var time = hours + ':' + minutes + ':' + seconds;
return time;
}
});
The following will run the filter every second. I am unable to get the filter to work as is, but it logs the updated date in the console so that you can at least see that it is being called each second.
This is just one way to do it. You could also apply the filter to the myDate variable within the controller and skip putting the filter in the markup.
angular.module('intervalExample', [])
.controller('ExampleController', ['$scope', '$interval',
function($scope, $interval) {
$scope.myDate = new Date();
var stop;
$scope.startTimer = function() {
stop = $interval(function() {
$scope.myDate = new Date();
}, 1000);
};
$scope.stopTimer = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
$scope.$on('$destroy', function() {
// Make sure that the interval is destroyed too
$scope.stopTimer();
});
$scope.startTimer();
}
])
.filter('DateGap', function() {
// In the return function, we must pass in a single parameter which will be the data we will work on.
// We have the ability to support multiple other parameters that can be passed into the filter optionally
return function update(input, optional1, optional2) {
console.log(input);
var t1 = new Date(input); // + 'Z');
var t2 = new Date();
var dif = t1.getTime() - t2.getTime();
var Seconds_from_T1_to_T2 = dif / 1000;
var Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
var sec_num = Math.floor(Seconds_Between_Dates); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (typeof PenddingHours != "undefined")
return hours >= PenddingHours ? true : false;
if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
var time = hours + ':' + minutes + ':' + seconds;
return time;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<div ng-app="intervalExample">
<div ng-controller="ExampleController">
Current time is: <span my-current-time="format"></span> {{ myDate | DateGap }}
</div>
</div>
You can't asynchronously change the output of a filter in angular 1.x. Filters are pure functions, the output depends only on the input which is a constant date in your case. Use a controller to handle the timing:
angular.module('intervalExample', [])
.controller('ExampleController', ['$scope', '$interval',
function($scope, $interval) {
$scope.myTime = 0;
var startTime = Date.now();
var timer = $interval(function() {
$scope.myTime = Date.now() - startTime;
}, 1000);
$scope.$on('$destroy', function() {
$interval.cancel(timer);
});
}
])
.filter('TimeSpan', function() {
// In the return function, we must pass in a single parameter which will be the data we will work on.
// We have the ability to support multiple other parameters that can be passed into the filter optionally
return function update(input, optional1, optional2) {
var dif = input;
var Seconds_from_T1_to_T2 = dif / 1000;
var Seconds_Between_Dates = Math.abs(Seconds_from_T1_to_T2);
var sec_num = Math.floor(Seconds_Between_Dates); // don't forget the second param
var hours = Math.floor(sec_num / 3600);
var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
var seconds = sec_num - (hours * 3600) - (minutes * 60);
if (typeof PenddingHours != "undefined")
return hours >= PenddingHours ? true : false;
if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
if (seconds < 10) {
seconds = "0" + seconds;
}
var time = hours + ':' + minutes + ':' + seconds;
return time;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<div ng-app="intervalExample">
<div ng-controller="ExampleController">
Current time is: <span my-current-time="format"></span> {{ myTime | TimeSpan }}
</div>
</div>

How to check if 10 minutes is passed in angularjs?

So that is my question. Is it possible to do something like this:
DateTime.Now > Date.AddMinutes(10) ?? Problem is that its now add minutes on date ? How can i do that?
I need to check if 10 minutes is passed...
var start = new Date();
if(start.addMinutes(10) > Date.now())
{
$scope.isCancelTicketButtonVisible = true;
$scope.$emit("appIsCancelTicketButtonVisible", $scope.isCancelTicketButtonVisible);
}
You can use this approach-
var firstDate = new Date();
var finalDate = firstDate.setMinutes(firstDate.getMinutes() + 10);
Update
html
<div ng-bind="elapsed()"></div>
code
var startDate = Date.Now();
function elapsed(){
if(start.addMinutes(10) > Date.now()){
console.log('do stuff here');
}
}
Use the $timeout service like below. After a 'sleep period' of 10 minutes the code will execute.
$timeout(function() {
console.log('Timeout fired')
$scope.isCancelTicketButtonVisible = true;
$scope.$emit("appIsCancelTicketButtonVisible", $scope.isCancelTicketButtonVisible);
}, 600000);
Reference

ExtJS setTimeout call function but after destroy view I get some errors

I have a function that after 3000 seconds call other function to check new data for a grid.
onViewDestroy: function(view) {
console.log('view destroy');
var me = this;
if(me.timer) clearTimeout(me.timer);
},
afterRender: function(){
// start timer to load log grid in 3 secs
me.logTimer = setTimeout(Ext.bind(me.checkProcessLog, me), 3000);
},
checkProcessLog: function() {
console.log('check process log');
var me = this,
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog(),
logStore = logGrid.getStore();
var selectedRecord = requestGrid.getSelectionModel().getSelection()[0];
// if the grid is expanded and the status of the process is pending or in progress
if (!logGrid.collapsed && (selectedRecord.get('Status') == 0 || selectedRecord.get('Status') == 1)) {
logStore.load({
callback: function(records, op, success) {
if (success) {
// start timer to load again log grid in 3 secs
me.logTimer = setTimeout(Ext.bind(me.checkProcessLog, me), 3000);
}
}
});
}
},
The problem that I have is that if I close my view (destroy) when the function has been called then all my variables such:
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog(),
logStore = logGrid.getStore();
I get the error:
Cannot read property 'getStore' of undefined
And that make sense because the view has been destroyed. Any workaround to avoid this?
A simple check will do it :
requestGrid = me.getRequestGrid(),
logGrid = me.getProcessLog();
if(!logGrid) return false
logStore = logGrid.getStore();
This also will stop the function beeing called.

Issues with using setTimeout inside of a for loop with arrays

--- UPDATE ---
Here is another attempt... The issue seems to be that the animation only happens to the third item in the for loop array I'm assuming because of the setTimeout delay. How can I have the animation fire for each item (3) with a delay?
// Breadcrumb Animation
(function(){
var header = document.getElementById("header"),
header2 = document.getElementById("header-secondary"),
banner = document.getElementById("banner");
var bcLink = [header, header2, banner];
var myTime = '';
var myItem = '';
function delay() {
setTimeout(fadeIn, myTime);
function fadeIn() {
if( myItem.style.opacity !== '1' ) {
console.log(myItem);
setInterval(function(){
myItem.style.opacity = parseFloat(myItem.style.opacity) + 0.1;
}, 100);
}
}
}
var i, len=bcLink.length;
for(var i = 0; i < len; i++) {
bcLink[i].style.opacity = 0;
myItem = bcLink[i];
myTime = myTime = parseInt(myTime + 2000)
delay();
}
})();
--- END UPDATE ---
The code below works but I was trying to optimize my code by creating a foor loop that will loop through each of the items (my attempt is commented out). I'm currently using 3 items on my page (header, header2, banner) that will load one after the other, but the second should not start until the first loads, the third should not start until the second item loads, and so on. This code will eventually be used for breadcrumbs where the amount of items will be uncertain but they will still load one after the other. Any help is greatly appreciated.
(function(){
var header = document.getElementById("header"),
header2 = document.getElementById("header-secondary"),
banner = document.getElementById("banner");
var bcLink = [header, header2, banner];
var myTime = '';
function fadeIn(name, speed) {
if( name.style.opacity !== '1' ) {
setInterval(function(){
name.style.opacity = parseFloat(name.style.opacity) + 0.1;
}, speed);
}
}
function delay(funct, time) {
setTimeout(function() {funct}, time);
}
bcLink[0].style.opacity = 0;
bcLink[1].style.opacity = 0;
bcLink[2].style.opacity = 0;
setTimeout(function(){fadeIn(bcLink[0], 100)}, 0);
setTimeout(function(){fadeIn(bcLink[1], 100)}, 1000);
setTimeout(function(){fadeIn(bcLink[2], 100)}, 2000);
// for(var i = 0; i < bcLink.length; i++) {
// bcLink[i].style.opacity = 0;
// delay(fadeIn(bcLink[i],100), i + 000);
// }
})();
use jQuery animation queue, not timeout.
http://blog.bigbinary.com/2010/02/02/understanding-jquery-effects-queue.html
Try changing:
delay(fadeIn(bcLink[i],100), i + 000);
to:
setTimeout(function(){fadeIn(bcLink[i], 100)}, i * 1000);

Resources