Meteor : How to clearInterval() onDestroyed() created in onRendered() - javascript

I have a countdown function to run every sec, So i proffered setInterval(). After I moved to another template, the interval function keep on running. How to destroy it onDestroyed(). Below code will help you to understand well.
<template name="Home">
<h4>{{timeremaining}}</h4>
</template>
Template.Home.helpers({
timeremaining : function(){
return Session.get('timeremaining');
}
});
Template.Home.onRendered(function () {
// time functions begin
var end_date = new Date(1476337380000); // I am getting timestamp from the db.
var run_every_sec = setInterval(function () {
var current_date = new Date();
var remaining = end_date.getTime() - current_date.getTime();
var oneDay = 24*60*60*1000;
var diffDays = Math.round(Math.abs(remaining/oneDay));
console.log(remaining); // am getting this log in every template.
if (remaining > 0) {
//set remaining timeLeft
Session.set('timeremaining',diffDays + ' Days ' + (Math.abs(end_date.getHours()-current_date.getHours())).toString() + ' Hrs ' + (Math.abs(end_date.getMinutes()-current_date.getMinutes())).toString() + ' Min ' + (60 - end_date.getSeconds()-current_date.getSeconds()).toString() + ' Sec ')
} else {
clearInterval(run_every_sec);
}
}, 1000);
//time functions end
}.bind(this));
Template.Home.onDestroyed(function () {
clearInterval(run_every_sec); // Here I cant remove this time interval
});
We can declare run_every_sec as global function. If so How to pass end_date. I dont think its wise idea to declare end_date inside the run_every_sec because its coming from db.

If you store the interval in file scope like Repo suggested, you'll have problems if there's ever more than one instance of the template at a time: both instances will use the same run_every_sec variable. In this case, you'll need to store the interval on the template instance, which can be accessed as this inside onRendered and onDestroyed:
Template.Home.onRendered(function () {
this.run_every_sec = setInterval(/* ... */);
});
Template.Home.onDestroyed(function () {
clearInterval(this.run_every_sec);
});
That way, each instance of the template will have its own run_every_sec property.

You should declare "run_every_sec" outside "onRendered".
So instead of this:
Template.Home.onRendered(function () {
// time functions begin
var end_date = new Date(1476337380000); // I am getting timestamp from the db.
var run_every_sec = setInterval(function () {
..do this:
var run_every_sec;
Template.Home.onRendered(function () {
// time functions begin
var end_date = new Date(1476337380000); // I am getting timestamp from the db.
run_every_sec = setInterval(function () {
then it will be available in "onDestroyed"

You should use Meteor's setInterval and clearInterval to make sure they run within a fiber. You can find more info here https://docs.meteor.com/api/timers.html.
var intervalID
Template.myTemplate.onRendered(function() {
intervalID = Meteor.setInterval(function() {
//do something
}, 1000)
})
Template.myTemplate.onDestroyed(function() {
Meteor.clearInterval(intervalID)
})

Related

JavaScript function isn’t being called (Firebase project)

When I fill out the table and hit submit everything updates except the two calculated values, tRemainder and minTillTrain. Pulling my hair out...
I can’t even get a window.alert() to show when I call the update() function, which makes me think the function cant be called. I would appreciate any feedback here. The only other file I have is a .html page, a basic web page with a table and an input form. You enter the required data and it adds and entry to the table, and the last two columns of each row are calculated values
edit: I think the issue is I'm never reaching the $('tbody tr') portion of the code. I can get console.log() and window.alert() everywhere but there
// $('#train-time').mask('00:00');
// $('#time-freq').mask('0#');
//initialize firebase
var config = {
apikey: "AIzaSyCxuLoAiSG_d69-43Tj43hLX6lgzg6Dq04",
authDomain: "train-schedule-451ab.firebaseapp.com",
databaseURL: "https://train-schedule-451ab.firebaseio.com",
projectId: "train-schedule-451ab",
storageBucket: "",
messagingSenderId: 559747835158,
};
firebase.initializeApp(config);
var database = firebase.database();
// Current Time
var updateTime = function () {
var currentTime = moment();
$("#current-time").html(moment(currentTime).format("H:mm:ss"));
};
setInterval(updateTime, 1000); // every second
//sumbit for new train
$("#btn-add").on("click", function () {
event.preventDefault();
//pushing newly submitted train data into firebase
database.ref().push({
name: $("#train-name").val().trim(),
destination: $("#train-destination").val().trim(),
starttime: $("#train-time").val().trim(),
frequency: $("#time-freq").val().trim()
});
$("#train-name").val('');
$("#train-destination").val('');
$("#train-time").val('');
$("#time-freq").val('');
});
database.ref().on("child_added", function (snapshot, prevChildKey) {
// // generate remove button
// var btn = $("<button>");
// btn.addClass("trash-btn");
// btn.attr("data-key", snapshot.key);
// var i = $("<i>");
// i.addClass("material-icons");
// i.text("delete")
// btn.append(i);
// btn.click(remove);
// generate table elements for new train
var $tr = $('<tr>').append(
$('<td>').text(snapshot.val().name),
$('<td>').text(snapshot.val().destination).addClass('destination'),
$('<td>').text(snapshot.val().starttime).addClass('starttime'),
$('<td>').text(snapshot.val().frequency).addClass('frequency'),
$('<td>').addClass('arrival'),
$('<td>').addClass('min-away'),
).appendTo('#train-table');
update();
});
//do an update function every minutes
var date = new Date();
setTimeout(function () {
setInterval(update, 60000);
update();
}, (60 - date.getSeconds()) * 1000);
function update() {
$('tbody tr').each(function () {
var frequency = $(this).find('.frequency').html();
var firstTime = $(this).find('.starttime').html();
// First Time (pushed back 1 year to make sure it comes before current time)
var firstTimeConverted = moment(firstTime, "hh:mm").subtract(1, "years");
// Current Time
var currentTime = moment();
// Difference between the times
var diffTime = moment().diff(moment(firstTimeConverted), "minutes");
// Time apart (remainder)
var tRemainder = diffTime % frequency;
// Minute Until Train
var minTillTrain = frequency - tRemainder;
// Next Train
var nextTrain = moment().add(minTillTrain, "hh:mm");
$(this).find('.arrival').html((nextTrain).format("hh:mm"));
$(this).find('.min-away').html(minTillTrain);
});
}

Store information in localStorage

I am trying to store information with the webstorage API. I created a class but the console returns the message "undefined". I was expecting the console to return 20 and 60. Can you help me identify my mistake? Thank you :)
class Timer {
constructor(minutes, secondes){
this.minutes = minutes;
this.secondes = secondes;
this.minutesEltHtml = document.getElementById("minutes");
this.secondesEltHtml = document.getElementById("secondes");
}
setMinutesSecondes(){
sessionStorage.setItem("minutes", this.minutes);
sessionStorage.setItem("secondes", this.secondes);
}
getMinutesSecondes(){
return sessionStorage.getItem("minutes");
return sessionStorage.getItem("secondes");
}
display(){
console.log(timer.getMinutesSecondes());
//console.log(timer.this.minutes);
}
}
let timer = new Timer(20, 60);
timer.display();
Line 20: undefined
1: You don't set the storage as you are not calling the method that does it. either call timer.setMinutesSecondes() before calling display or do so in the constructor as per my example below.
2: It's seconds not secondes (sorry for the pedantry).
3: Your getMinutesSecondes function has 2 return calls. Execution will stop after the first call. see my example below.
4: Some of the mistakes here indicate you would benefit from some introduction to JavaScript courses. Have a quick google, there is a wealth of free content online.
class Timer {
constructor(minutes, secondes){
this.minutes = minutes;
this.secondes = secondes;
this.minutesEltHtml = document.getElementById("minutes");
this.secondesEltHtml = document.getElementById("secondes");
this.setMinutesSecondes();
}
setMinutesSecondes(){
sessionStorage.setItem("minutes", this.minutes);
sessionStorage.setItem("secondes", this.secondes);
}
getMinutesSecondes(){
return { // you were calling return twice... only the first line would have returned
mintues: sessionStorage.getItem("minutes"),
secondes: sessionStorage.getItem("secondes")
};
}
display(){
console.log(this.getMinutesSecondes());
}
}
let timer = new Timer(20, 60);
timer.display(); // output {minutes:20, secondes:60}
The reason you are getting undefined as the return value of sessionStorage.getItem is because the value you are trying to retrieve from the storage has not be set on that storage. You defined a method to store the data to the storage without calling that method ( setMinutesSecondes ).
Do this instead ( Assuming the code you presented is your complete code )
class Timer {
constructor(minutes, secondes){
this.minutes = minutes;
this.secondes = secondes;
this.minutesEltHtml = document.getElementById("minutes");
this.secondesEltHtml = document.getElementById("secondes");
}
setMinutesSecondes(){
sessionStorage.setItem("minutes", this.minutes);
sessionStorage.setItem("secondes", this.secondes);
}
getMinutesSecondes(){
return {
minutes: sessionStorage.getItem("minutes"),
secondes: sessionStorage.getItem("secondes");
};
}
display(){
console.log(timer.getMinutesSecondes());
//console.log(timer.this.minutes);
}
}
let timer = new Timer(20, 60);
timer.setMinutesSecondes(); // if you don't wish to call this method here, you can still call this method in the constructor function
timer.display();

Callback generates "TypeError: this is undefined" in Angular2/Firebase

I'm trying to understand what is gong on here and why I'm getting an error if I call a function a certain way and not getting the error when i call the function a different way. Here is the way that produces the error first:
player.service.ts file
in the #Injectable i have
private roomsRef: Firebase;
constructor() {
this.roomsRef = new Firebase("https://xxx.firebaseio.com/rooms");
}
postGameActions(roomId: string) {
console.log("post game actions start on " + roomId);
//error on below 'this'
this.roomsRef.child(roomId).child("board").once("value", snap => {
let board = snap.val();
//do stuff with 'board' object
});
}
room.component.ts file
activateTimer(roomId: string, roomName: string, callback) {
var timer = setInterval(redirectTimer, 1000);
var seconds: number = 5;
var timerHtml = document.getElementById("divRedirectTimer" + roomName);
timerHtml.innerHTML = "[" + seconds + "]";
function redirectTimer() {
timerHtml.innerHTML = "[" + (seconds--) + "]";
if(seconds === 0) {
clearInterval(timer);
callback(roomId);
}
};
}
call non-working version like this
activateTimer(room.id, room.name, _playerService.postGameActions)
Error:
EXCEPTION: TypeError: this is undefined
Working version
Works fine when like this but no use of setInterval() since activateTimer just calls the service method
room.component.ts file
activateTimer(roomId: string, roomName: string) {
var timer = setInterval(redirectTimer, 1000);
var seconds: number = 5;
var timerHtml = document.getElementById("divRedirectTimer" + roomName);
timerHtml.innerHTML = "[" + seconds + "]";
function redirectTimer() {
timerHtml.innerHTML = "[" + (seconds--) + "]";
if(seconds === 0) {
clearInterval(timer);
}
}
this._playerService.postGameActions(roomId);
call working version like this
activateTimer(room.id, room.name)
Why is the 'this' undefined when i call postGameActions as a callback? I'm sure I'm missing something simple here
You need to wrap the call into an arrow function:
activateTimer(room.id, room.name, () => {
_playerService.postGameActions();
});
The problem in your code is that you reference directly a function so you lose the object it will be executed on.
Another way would be to use the bind method:
activateTimer(room.id, room.name, _playerService.postGameActions.bind(_playerService);

How do I get the gender from a particular user when updating a different table? Azure mobile services

I have a table called Subscription and another table called Client I need the gender of the Client who owns the subscription every time I make an update. Here's my update script:
function update(item, user, request) {
var subscriptionId = item.id;
var subscriptionActivitiesTable = tables.getTable("SubscriptionActivity");
var userTable = tables.getTable("User");
var activityTable = tables.getTable("Activity");
var userGender = userTable.where({id: item.UserId}).select('Gender').take(1).read();
console.log(userGender);
activityTable.where({PlanId:item.PlanId, Difficulty: item.Difficulty}).read({
success: function(results){
var startDate = item.StartDate;
results.forEach(function(activity)
{
var testDate = new Date(startDate.getFullYear(),startDate.getMonth(), startDate.getDate());
testDate.setDate(testDate.getDate() + activity.Sequence + (activity.Week*7));
subscriptionActivitiesTable.insert({SubscriptionId: subscriptionId,
ActivityId: activity.id, ShowDate: new Date(testDate.getFullYear(),
testDate.getMonth(), testDate.getDate()), CreationDate: new Date()});
})
}
});
var planWeeks = 12;//VER DE DONDE SACAMOS ESTE NUMERO
var idealWeight = 0;
if (userGender === "Male")
{
idealWeight = (21.7 * Math.pow(parseInt(item.Height)/100,2));
}
else
{
idealWeight = (23 * Math.pow(parseInt(item.Height)/100,2));
}
var metabolismoBasal = idealWeight * 0.95 * 24;
var ADE = 0.1 * metabolismoBasal;
var activityFactor;
if (item.Difficulty === "Easy")
{
activityFactor = 1.25;
}
else if(item.Difficulty === "Medium")
{
activityFactor = 1.5;
}
else
{
activityFactor = 1.75;
}
var caloricRequirement = ((metabolismoBasal + ADE)*activityFactor);
activityTable.where(function(item, caloricRequirement){
return this.PlanId === item.PlanId && this.Type != "Sport" &&
this.CaloricRequirementMin <= caloricRequirement &&
this.CaloricRequirementMax >= caloricRequirement;}, item, caloricRequirement).read({
success: function(results)
{
var startDate = item.StartDate;
results.forEach(function(activity)
{
for (var i=0;i<planWeeks;i++)
{
var testDate = new Date(startDate.getFullYear(),startDate.getMonth(), startDate.getDate());
testDate.setDate(testDate.getDate() + activity.Sequence + (i*7));
subscriptionActivitiesTable.insert({SubscriptionId: subscriptionId,
ActivityId: activity.id, ShowDate: new Date(testDate.getFullYear(),
testDate.getMonth(), testDate.getDate()), CreationDate: new Date()});
}
})
}
})
request.execute();
}
I tried the code above and clientGender is undefined. As you can see I want to use the gender to set the idealWeight.
The read() method expects a function to be passed in on the success parameter - it doesn't return the result of the query like you'd think.
Try something like this instead:
function update(item, user, request) {
var clientTable = tables.getTable("Client");
var clientGender = 'DEFAULT';
clientTable.where({id: item.ClientId}).select('Gender').take(1).read({
success: function(clients) {
if (clients.length == 0) {
console.error('Unable to find client for id ' + item.ClientId);
} else {
var client = client[0];
clientGender = client.Gender;
// since we're inside the success function, we can continue to
// use the clientGender as it will reflect the correct value
// as retrieved from the database
console.log('INSIDE: ' + clientGender);
}
}
});
// this is going to get called while the clientTable query above is
// still running and will most likely show a value of DEFAULT
console.log('OUTSIDE: ' + clientGender);
}
In this sample, the client table query is kicked off, with a callback function provided in the success parameter. When the query is finished, the callback function is called, and the resulting data is displayed to the log. Meanwhile - while the query is still running, that is - the next statement after the where/take/select/read fluent code is run, another console.log statment is executed to show the value of the clientGender field outside the read function. This code will run while the read statement is still waiting on the database. Your output should look something like this in the WAMS log:
* INSIDE: Male
* OUTSIDE: Default
Since the log shows the oldest entries at the bottom, you can see that the OUTSIDE log entry was written sometime before the INSIDE log.
If you're not used to async or functional programming, this might look weird, but as far as I've found, this is now node works. Functions nested in functions nested in functions can get kind of scary, but if you plan ahead, it probably won't be too bad :-)

Preventing a callback from executing until input stops

A timer to fire an AJAX call if no key is pressed. If a key is pressed, then abort the last timer and add a new timer. That is what I want to do but failed to success. Here is my code:
var t;
input.onkeyup = function(){
$('.confirmText').html('Checking...');
var timeStampObj = new Date()
var timeStamp = timeStampObj.getTime();
var oldTimeStamp = $(this).attr('timeStamp');//I store a timeStamp in the element
if(timeStamp < 500 + oldTimeStamp){
$(this).attr('timeStamp', timeStamp);
clearTimeout(t);
}
t = setTimeout(function(){
$.ajax({
url: 'serverScripts/settings/checkEmailAvailability.php',
data: 'email='+email,
success: function(text){
if(text == 'available'){
$('.confirmText').html('Available!');
}else{
$('.confirmText').html('Occupied!');
}
}
});
}, 500);//Half a second
$(this).attr('timeStamp', timeStamp);
}
It sounds like you're asking for a debouncer. The term comes from electronics. It's a way to prevent multiple events from firing within some time threshold. You can use the following function to create a new function that will only be called if a given amount of time has passed since the last event.
function debounce(callback, timeout, _this) {
var timer;
return function(e) {
var _that = this;
if (timer)
clearTimeout(timer);
timer = setTimeout(function() {
callback.call(_this || _that, e);
}, timeout);
}
}
// requires jQuery
$("div").click(debounce(function() {
console.log("tset");
}, 2000));
The callback given to debounce won't execute as long as click events keep firing.
The excellent Underscore.js library includes an equivalent function and there are at least a couple jQuery plugins:
http://code.google.com/p/jquery-debounce/
http://benalman.com/code/projects/jquery-dotimeout/examples/debouncing/
Where do you define the email variable in your JavaScript?
You need to define email somewhere and then check to see if your script works.
var email = $(this).value; // Pseudo-code - are you using jQuery?

Categories

Resources