Vis.js timline not rendering in Meteor.js application - meteor

I'm trying to include the vis.js library into my Meteor application. I have the library included and I'm rendering the page without errors, however, the vis.js timeline is rendering with only horizontal lines, and no objects.
I am definitely populating my dataset as I have confirmed it via console.
Here is my code.
Template.events_timeline.rendered = function () {
drawTimeline();
};
function drawTimeline(){
var container = document.getElementById('timeline');
var event = Events.find();
if (event.count() === 0) {
console.log('found no events');
return;
}
data = new vis.DataSet({
type: { start: 'ISODate', end: 'ISODate' }
});
var i = 1;
event.forEach(function (ev) {
data.add([{id: i, content: ev.content, start: ev.startDate, end: ev.endDate}]);
i++;
});
// Configuration for the Timeline
var options = {};
// Create a Timeline
var timeline = new vis.Timeline(container, data, options);
}
My template is as follows. Very basic.
<template name="events_timeline">
<div id="timeline"></div>
</template>
Result.
http://i.imgur.com/k2HyQb5.jpg
I was able to get the graph to render with data on it's own with the above code and some changes to the router, but I cannot seem to get the chart to work when embedding the chart template in, for example, a layout template.
The following router changes renders the chart correctly, with data from the Events collection.
this.route('events_timeline', {
path: '/timeline',
waitOn: function() {
return Meteor.subscribe('events');
},
notFoundTemplate:'data_not_found',
action: function() {
if(this.ready()) {
this.setLayout('events_timeline');
this.render();
} else {
this.setLayout('loading');
this.render('loading');
}
}
});
If I replace ...
this.setLayout('events_timeline');
... With ...
this.setLayout('layout');
... Then I still get the blank chart.
I think I'm missing something with how to correctly waitOn the data for a specific template.

Related

Fullcalendar using resources as a function with select menu

Using Fullcalendar 4, I am trying to show/hide my resources using a select menu. When the user selects one of the providers from a menu, I want to only show that one resourc's events.
Above my fullcalendar I have my select menu:
<select id="toggle_providers_calendar" class="form-control" >
<option value="1" selected>Screech Powers</option>
<option value="2">Slater</option>
</select>
I am gathering the resources I need using an ajax call on my included fullcalendar.php page. I am storing them in an object and then trying to control which resources are shown onscreen:
document.addEventListener('DOMContentLoaded', function() {
var resourceData = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
console.log(resourceData);
});
//below, set the visible resources to whatever is selected in the menu
//using 1 in order for that to show at start
var visibleResourceIds = ["1"];
//below, get the selected id when the the menu is changed and use that in the toggle resource function
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
var calendar_full = document.getElementById('calendar_full');
var calendar = new FullCalendar.Calendar(calendar_full, {
events: {
url: 'ajax_get_json.php?what=location_appointments'
},
height: 700,
resources: function(fetchInfo, successCallback, failureCallback) {
// below, I am trying to filter resources by whether their id is in visibleResourceIds.
var filteredResources = [];
filteredResources = resourceData.filter(function(x) {
return visibleResourceIds.indexOf(x.id) !== -1;
});
successCallback(filteredResources);
},
...
});
// below, my toggle_providers_calendar will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
To make sure the getJSON is working, I have console.log(resourceData). The information in the console once it's gathered is:
[{id: '1', title: 'Screech Powers'}, {id: '2', title: 'Slater}]
... the above are the correct resources that can be chosen/rendered. So that seems to be okay.
On page load, no resources show at all, when resource id of '1' (Screech Powers) should be shown per my code. Well, at least, that's what I am trying to do right now.
When the menu changes, resources will show/hide, but not based on what's selected; the logic of only showing what is selected in the menu doesn't seem to be working.
I used to use a URL request for my resources: 'ajax_get_json.php?what=schedule_providers_at_location', and it worked fine! All resources show then their events properly. I am just trying to modify it by using a menu to show/hide the resources as needed.
Here's what I'm doing to make it happen so far! In case someone comes across this post ever, this will help.
Here's my code before my fullcalendar code.
var resourceData = [];
var visibleResourceIds = [];
$.getJSON('ajax_get_json.php?what=schedule_providers_at_location',
function(data) {
$.each(data, function(index) {
resourceData.push({
id: data[index].value,
title: data[index].text
});
});
});
$('#toggle_providers_calendar').change(function() {
toggleResource($('#toggle_providers_calendar').val());
});
My select menu with id 'toggle_providers_calendar' is the same as my original post. My fullcalendar resources as a function is the same too.
After the calendar is rendered, here are the changes I made to my toggle resources function:
// menu button/dropdown will trigger this function. Feed it resourceId.
function toggleResource(resourceId) {
visibleResourceIds = [];
//if select all... see if undefined from loading on initial load = true
if ((resourceId == '') || (resourceId === undefined)) {
$.map( resourceData, function( value, index ) {
visibleResourceIds.push(value.id);
});
}
var index = visibleResourceIds.indexOf(resourceId);
if (index !== -1) {
visibleResourceIds.splice(index, 1);
} else {
visibleResourceIds.push(resourceId);
}
calendar.refetchResources();
}
This causes the resources to show and hide properly. If the user selects "Show All" that works too!
In order to have a default resource show on load, I add this to my fullcalendar script:
loading: function(bool) {
if (bool) {
//insert code if still loading
$('.loader').show();
} else {
$('.loader').hide();
if (initial_load) {
initial_load = false;
//code here once done loading and initial_load = true
var default_resource_to_show = "<?php echo $default_provider; ?>";
if (default_resource_to_show) {
//set the menu to that provider and trigger the change event to toggleresrource()
$('#toggle_providers_calendar').val(default_provider).change();
} else {
//pass in nothing meaning 'select all' providers for scheduler to see
toggleResource();
}
}
}
},
I am using a bool variable of initial_load to see if the page was just loaded (basically not loading data without a page refresh). The bool of initial_load = true is set outside of DOMContentLoaded
<script>
//show selected date in title box
var initial_load = true;
document.addEventListener('DOMContentLoaded', function() {
My only current problem is that when toggleResource function is called, the all day vertical time block boundaries don't line up with the rest of the scheduler. Once I start navigating, they do, but I don't understand why it looks like this on initial load or when toggleResource() is called:
Any thoughts on how to correct the alignment of the allday vertical blocks?

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();
};

Meteor.renderList alway end up in [elseFunc]

I'm new to Meteor.
Trying to render items from collection but Meteor.renderList(observable, docFunc, [elseFunc]) alway go to elseFunc.
this.ComponentViewOrdersFlow = Backbone.View.extend({
template: null,
initialize: function() {
var frag;
Template.ordersFlow.events = {
"click a": function(e) {
return App.router.aReplace(e);
}
};
this.template = Meteor.render(function() {
return Template.ordersFlow();
});
console.log(Colors);
frag = Meteor.renderList(
Colors.find(),
function(color) {
console.log(color);
},
function() {
console.log('else consdition');
}
);
},
render: function() {
this.$el.html(this.template);
return this;
}
});
Initially I thought that Collection is empty, but console.log(Colors) shows that there are items in collection. Moreover if I use Meteor.render(... -> Template.colors({colors: Colors.find()}) ) it renders template end show Collection items there.
Meteor version 0.6.6.3 (Windows 7, 64bit)
Mongo - connected to MongoLab
Thank you for any help.
Jev.
Can't really explain this well in the comments, so here is a very, very simple example of using the Meteor template engine. This is a 100% functional app, showcasing basic reactivity. Note that I never call render() or renderList() anywhere.
All this app does is show a button, that when clicked, adds a number to a list. The number is reactively added to the list, even though I never do anything to make that reactivity explicit. Meteor's templates are automatically reactive! Try it out - this is all of the code.
numbers.html:
<body>
{{> numberList}}
</body>
<template name="numberList">
<ul>
{{#each numbers}}
<li>{{number}}</li>
{{/each}}
</ul>
<button>Click Me</button>
</template>
numbers.js:
var Numbers = new Meteor.Collection("numbers");
if (Meteor.isClient) {
Template.numberList.numbers = function() {
return Numbers.find();
};
var idx = 0;
Template.numberList.events({
"click button": function() {
Numbers.insert({
number: idx
});
idx++;
}
});
}

Resources