METEOR - Show loader and run methods at specific time - meteor

I wanna build a loader circle what goes from 1 to 100% and in the meantime to run some methods.
loader circle
The scenario is:
load the page and start counting.
When the counter is at 50% pause counting and run the first method and when I have the result to start counting from where I was left.
count until 90% and run the second method.
I was trying something with Meteor.setInterval on onCreated but I'm not sure if it's the right method to do this.
Can someone give me some ideas about how to approach this?
Thanks!

There are several ways you can do this depending on your specific needs and you might even want to use one of the many Reactive Timer packages that are out there.
Here is one working example that only uses the Meteor API (no packages). Note, I did not actually incorporate the loader circle animation since it wasn't specifically part of the question.
Template definition
<template name="loader">
<h1>Loading...({{loadingPercentage}})</h1>
</template>
Template logic
Template.loader.onCreated(function() {
// used to update loader circle and triggering method invocation
this.elapsedTime = new ReactiveVar(0);
// handle for the timer
var timerHandle;
// starts a timer that runs every second that can be paused or stopped later
const startTimer = () => {
timerHandle = Meteor.setInterval(() => {
this.elapsedTime.set(this.elapsedTime.get() + 1);
}, 1000);
};
// pauses/stops the timer
const pauseTimer = () => {
Meteor.clearInterval(timerHandle);
};
// let's first start our timer
startTimer();
// runs every second and triggers methods as needed
this.autorun(() => {
const elapsedTime = this.elapsedTime.get();
// call methods based upon how much time has elapsed
if (elapsedTime === 5) {
pauseTimer();
// call method and restart the timer once it completes
Meteor.call('methodOne', function(error, result) {
// do stuff with the result
startTimer();
});
} else if (elapsedTime === 9) {
pauseTimer();
// call method and restart the timer once it completes
Meteor.call('methodOne', function(error, result) {
// do stuff with the result
// DO NOT RESTART TIMER!
});
}
});
});
Template.loader.helpers({
// helper used to show elapsed time on the page
loadingPercentage: function() {
return Template.instance().elapsedTime.get();
},
});
Let me know if you have any questions.

This is what i was trying to do:
Template.onboarding.onCreated(function(){
var instance = this;
instance.progress = new ReactiveVar(0);
instance.autorun(function(){
var loader = {
maxPercent: 100,
minPercent: instance.progress.get(),
start: function(){
var self = this;
this.interval = Meteor.setInterval(function(){
self.minPercent += 1;
if(self.minPercent >= self.maxPercent) {
loader.pause();
}
if( self.minPercent == 25) {
loader.pause();
Meteor.call('runMethodOne', (err,res)=>{
if (!err) loader.resume();
});
}
if(self.minPercent == 75) {
loader.pause();
Meteor.call('runMethodTwo', (err,res) =>{
if(res) loader.resume();
});
}
}
});
}
instance.progress.set(self.minPercent)
},50);
},
pause: function(){
Meteor.clearInterval(this.interval);
delete this.interval;
},
resume: function(){
if(!this.interval) this.start();
}
};
loader.start();
}
});
});

Related

meteor emit event then used by aframe eventlistener

I have an aframe object in main.html:
<a-gltf-model id='player1' foobox playmyclip src="#myMixBun" ></a-gltf-model>
I want to have it perform a gltf clip animation when the eventListener ‘driveplay’ is emitted.
In foofile.js:
AFRAME.registerComponent('playmyclip', {
init: function () {
var el = this.el;
el.addEventListener('driveplay', function () {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
});
}
});
Currently I have it so when the j key is hit ‘driveplay’ is emitted:
AFRAME.registerComponent('foobox', {
init: function() {
var el = this.el
var swingno = 0;
self = this;
document.addEventListener('keydown', (event) => {
const keyName = event.key;
if (keyName === 'j') {
el.emit('driveplay',{},true);
// code to store record of event in Mongo for second player
var playerid = self.el.getAttribute('id');
var playerMid = Games.findOne({name: playerid})._id;
Games.update({_id: playerMid},{$set:{swings : swingno}});
swingno = swingno + 1;
}
})
}
});
But I also need to have the animation to happen at the same time on player2‘s screen. So in the above code I increment a variable in Mongo every time the animation happens. Then in main.js meteor automatically emits an event whenever “swingno” changes in Mongo.
main.js:
import {EventEmitter} from 'meteor/raix:eventemitter';
Event = new EventEmitter();
var swingcnt1 = 0;
Template.hello.helpers({
counter() {
if (Games.findOne()) //mongo is ready to access
{
var plyr1Swing = Games.findOne({name: "player1"}).swings;
if (plyr1Swing !== swingcnt1) {
Event.emit('driveplay',{},true);
swingcnt1 = plyr1Swing;
console.log(“this shows on player2’s console automatically”, swingcnt1);
}
}
else {null}
return { ........};
},
When player1 hits the “j” key the animation happens correctly on his screen, plus the event is recorded in Mongo, and player2 receives the updated Mongo value (since it show in the console.log).
The problem is the
Event.emit('driveplay',{},true);
statement doesn’t trigger the animation in player2‘s screen. This is a little tricky, since I need meteor’s “raix:eventemitter” package to create an event that the aframe event listener can see. It’s possible I’m not actually emitting an event at all, since I don’t know how to test if it’s working. Or possibly aframe can’t see the emitted event.
Possibly there’s an easier way of doing this. Thanks for any help.
SOLVED The solution was to use the listener code from meteor’s raix:eventemitter package inside Aframe’s component.
https://atmospherejs.com/raix/eventemitter
AFRAME.registerComponent('playmyclip', {
init: function ()
var el = this.el;
listener = function() {
el.setAttribute('animation-mixer', {clip: 'Drive', loop: 'once'});
};
Event.on('driveplay', listener);
}
});
Plus the foobox component no longer needs:
if (keyName === 'j') {
el.emit('driveplay',{},true);
Both player1 and player2 get the animation event from
Event.emit('driveplay',{},true);
in main.js when the meteor helper notices a change in the mongodb.

Meteor - Script doesn't load Web Audio Buffers properly on refresh / only on certain routes

https://github.com/futureRobin/meteorAudioIssues
Trying to load audio buffers into memory. When I hit localhost:3000/tides or localhost:3000 it loads my buffers into memory with no problems. When I then click through onto a session e.g. localhost:3000/tides/SOMESESSIONID. the buffers have already loaded from the previous state.
However, when I then refresh the page on "localhost:3000/tides/SOMESESSIONID" the buffers don't load properly and the console just logs an array of file path names.
Crucial to app functionality. Any help would be great!
audio.js
//new context for loadKit
var context = new AudioContext();
var audioContext = null;
var scheduleAheadTime = 0;
var current16thNote = 0;
var bpm = 140;
//array of samples to load first.
var samplesToLoad = [
"ghost_kick.wav", "ghost_snare.wav", "zap.wav", "ghost_knock.wav"
];
//create a class called loadKit for loading the sounds.
function loadKit(inputArg) {
//get the array of 6 file paths from input.
this.drumPath = inputArg;
}
//load prototype runs loadsample function.
loadKit.prototype.load = function() {
//when we call load, call loadsample 6 times
//feed it the id and drumPath index value
for (var i = 0; i < 6; i++) {
this.loadSample(i, this.drumPath[i]);
}
};
//array to hold the samples in.
//now loadKitInstance.kickBuffer will hold the buffer.
var buffers = [
function(buffer) {
this.buffer1 = buffer;
},
function(buffer) {
this.buffer2 = buffer;
},
function(buffer) {
this.buffer3 = buffer;
},
function(buffer) {
this.buffer4 = buffer;
},
function(buffer) {
this.buffer5 = buffer;
},
function(buffer) {
this.buffer6 = buffer;
}
];
//load in the samples.
loadKit.prototype.loadSample = function(id, url) {
//new XML request.
var request = new XMLHttpRequest();
//load the url & set response to arraybuffer
request.open("GET", url, true);
request.responseType = "arraybuffer";
//save the result to sample
var sample = this;
//once loaded decode the output & bind to the buffers array
request.onload = function() {
buffers[id].bind("");
context.decodeAudioData(request.response, buffers[id].bind(sample));
}
//send the request.
request.send();
};
//get the list of drums from the beat.json
//load them into a the var 'loadedkit'.
loadDrums = function(listOfSamples) {
var drums = samplesToLoad;
loadedKit = new loadKit(listOfSamples);
loadedKit.load();
console.log(loadedKit);
}
//create a new audio context.
initContext = function() {
try {
//create new Audio Context, global.
sampleContext = new AudioContext();
//create new Tuna instance, global
console.log("web audio context loaded");
} catch (e) {
//if not then alert
alert('Sorry, your browser does not support the Web Audio API.');
}
}
//inital function, ran on window load.
init = function() {
audioContext = new AudioContext();
timerWorker = new Worker("/timer_worker.js");
}
client/main.js
Meteor.startup(function() {
Meteor.startup(function() {
init();
initContext();
});
router.js
Router.route('/', {
template: 'myTemplate',
subscriptions: function() {
this.subscribe('sessions').wait();
},
// Subscriptions or other things we want to "wait" on. This also
// automatically uses the loading hook. That's the only difference between
// this option and the subscriptions option above.
waitOn: function () {
return Meteor.subscribe('sessions');
},
// A data function that can be used to automatically set the data context for
// our layout. This function can also be used by hooks and plugins. For
// example, the "dataNotFound" plugin calls this function to see if it
// returns a null value, and if so, renders the not found template.
data: function () {
return Sessions.findOne({});
},
action: function () {
loadDrums(["ghost_kick.wav", "ghost_snare.wav", "zap.wav", "ghost_knock.wav"]);
// render all templates and regions for this route
this.render();
}
});
Router.route('/tides/:_id',{
template: 'idTemplate',
// a place to put your subscriptions
subscriptions: function() {
this.subscribe('sessions', this.params._id).wait();
},
// Subscriptions or other things we want to "wait" on. This also
// automatically uses the loading hook. That's the only difference between
// this option and the subscriptions option above.
waitOn: function () {
return Meteor.subscribe('sessions');
},
// A data function that can be used to automatically set the data context for
// our layout. This function can also be used by hooks and plugins. For
// example, the "dataNotFound" plugin calls this function to see if it
// returns a null value, and if so, renders the not found template.
data: function (params) {
return Sessions.findOne(this.params._id);
},
action: function () {
console.log("IN ACTION")
console.log(Sessions.findOne(this.params._id));
var samples = Sessions.findOne(this.params._id)["sampleList"];
console.log(samples);
loadDrums(samples);
// render all templates and regions for this route
this.render();
}
})
Okay so i got a reply on the meteor forums!
https://forums.meteor.com/t/script-doesnt-load-web-audio-buffers-properly-on--id-routes/15270
"it looks like your problem is relative paths, it's trying to load your files from localhost:3000/tides/ghost_*.wav if you change line 58 of your router to go up a directory for each file it should work.
loadDrums(["../ghost_kick.wav", "../ghost_snare.wav", "../zap.wav", "../ghost_knock.wav"]);
This did the trick. Seems odd that Meteor can load stuff fine without using '../' in one route but not in another but there we go. Hope this helps someone in the future.

Why is data set with Meteor Iron Router not available in the template rendered callback?

This is a bit puzzling to me. I set data in the router (which I'm using very simply intentionally at this stage of my project), as follows :
Router.route('/groups/:_id',function() {
this.render('groupPage', {
data : function() {
return Groups.findOne({_id : this.params._id});
}
}, { sort : {time: -1} } );
});
The data you would expect, is now available in the template helpers, but if I have a look at 'this' in the rendered function its null
Template.groupPage.rendered = function() {
console.log(this);
};
I'd love to understand why (presuming its an expected result), or If its something I'm doing / not doing that causes this?
From my experience, this isn't uncommon. Below is how I handle it in my routes.
From what I understand, the template gets rendered client-side while the client is subscribing, so the null is actually what data is available.
Once the client recieves data from the subscription (server), it is added to the collection which causes the template to re-render.
Below is the pattern I use for routes. Notice the if(!this.ready()) return;
which handles the no data situation.
Router.route('landing', {
path: '/b/:b/:brandId/:template',
onAfterAction: function() {
if (this.title) document.title = this.title;
},
data: function() {
if(!this.ready()) return;
var brand = Brands.findOne(this.params.brandId);
if (!brand) return false;
this.title = brand.title;
return brand;
},
waitOn: function() {
return [
Meteor.subscribe('landingPageByBrandId', this.params.brandId),
Meteor.subscribe('myProfile'), // For verification
];
},
});
Issue
I was experiencing this myself today. I believe that there is a race condition between the Template.rendered callback and the iron router data function. I have since raised a question as an IronRouter issue on github to deal with the core issue.
In the meantime, workarounds:
Option 1: Wrap your code in a window.setTimeout()
Template.groupPage.rendered = function() {
var data_context = this.data;
window.setTimeout(function() {
console.log(data_context);
}, 100);
};
Option 2: Wrap your code in a this.autorun()
Template.groupPage.rendered = function() {
var data_context = this.data;
this.autorun(function() {
console.log(data_context);
});
};
Note: in this option, the function will run every time that the template's data context changes! The autorun will be destroyed along with the template though, unlike Tracker.autorun calls.

Is it possible to display a UI transition to reflect changes in a collection with meteorjs?

I would like to display a pulse transition when my collection change.
In my html file, I have that:
<template name="menuItemTag">
<div class="app-menu-item-tag ui label">{{ count }}</div>
</template>
In my js file, I expose the count variable for my template like that:
Template.menuItemTag.count = function() {
return MyCollection.find().count();
};
With that the count change in the ui when the collection is updated.
Now, I would like to display a pulse transition on my div label each time the count value change.
I tried to use the cursor.observe
Template.menuItemTag.rendered = function() {
MyCollection.find().observe({
added: function (id, user) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
},
removed: function () {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
});
};
Unfortunately, it is call too many times when the template is rendered the first time.
If initialy I have 40 items in my collection, the transition is played 40 times...
Is there a clean way for playing a ui transition on changes and avoid the collection initialisation?
Try this:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
$('.app-menu-item-tag:nth(0)').transition('pulse');
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};
Thanks sbking for your answer, I still have a problem on initialization of the collection.
I propose below to defer the first animation util the collection will be completely filled:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
var that = this;
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
// Cancel playing UI transition. The collection is not completely initialized
if (that.handleTimeout) {
Meteor.clearTimeout(that.handleTimeout);
}
// Play the UI transition without the timeout if the collection is initialized
if (that.noNeedTimeoutAnymore) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
// Tentative to play the UI transition during the collection feeding
else {
that.handleTimeout = Meteor.setTimeout(function() {
$('.app-menu-item-tag:nth(0)').transition('pulse');
// At this point we know that the collection is totaly initialized
// then we can remove the timeout on animation for the future update
that.noNeedTimeoutAnymore = true;
}, 1500); // You can adjust the delay if needed
}
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};

jquery disable a button for a specific time

i want to disable a button for a specific time. how can i do that?
Since this is likely to be a task you might like to repeat, I think the best way to do this would be to extend jQuery like so:
$.fn.timedDisable = function(time) {
if (time == null) { time = 5000; }
return $(this).each(function() {
$(this).attr('disabled', 'disabled');
var disabledElem = $(this);
setTimeout(function() {
disabledElem.removeAttr('disabled');
}, time);
});
};
This will allow you to call a function on a set of matched elements which will temporarily disable them. As it is written, you can simply call the function, and the selected elements will be disabled for 5 seconds. You would do that like so:
$('#some-button').timedDisable();
You can adjust the default time setting by changing the 5000 in the following line:
if (time == null) { time = 5000; }
You can optionally pass in a time value in milliseconds to control how long the elements will be disabled for. For example:
$('#some-button').timedDisable(1000);
Here's a working demo: http://jsfiddle.net/fG2ES/
Disable the button and then use setTimeout to run a function that enables the button after a few seconds.
$('#some-button').attr("disabled", "disabled");
setTimeout('enableButton()', 5000);
function enableButton(){
$('#some-button').removeAttr('disabled');
}
Try this.
(function(){
$('button').on('click',function(){
var $this=$(this);
$this
.attr('disabled','disabled');
setTimeout(function() {
$this.removeAttr('disabled');
}, 3000);
});
})();
You can find a working example here http://jsfiddle.net/informativejavascript/AMqb5/
Might not be the most elegant solution, but I thought I'd play with jQuery queues on this one...
​$.fn.disableFor = function (time) {
var el = this, qname = 'disqueue';
el.queue(qname, function () {
el.attr('disabled', 'disabled');
setTimeout( function () {
el.dequeue(qname);
}, time || 3000);
})
.queue(qname, function () {
el.removeAttr('disabled');
})
.dequeue(qname);
};
$('#btn').click( function () {
​$(this).disableFor(2000);​​​​
});
​
This is where I worked it out... http://jsfiddle.net/T9QJM/
And, for reference, How do I chain or queue custom functions using JQuery?

Resources