I have a Meteor app using Blaze templates designed to power a diary, with the display part handled by JQuery FullCalendar. I am passing fullcalendar a callback to use for the events to display, which is working well. I can't get this to behave in a reactive fashion though - the diary will be used on several devices simultaneously and I want the event to be updated on all devices immediately. I am able to update the collection, I can see the changes being pushed to the minimongo subscription however I can't get the calendar to update. Is there an event I can hook into where I can force the calendar to refetch the events? It seems like this should be a fairly common use case?
Current code is as follows:
Diary.js:
Template.Diary.onCreated(function() {
this.autorun(() => {
this.subscribe('bookings');
});
});
Template.Diary.helpers({
dataReady : function() {
return Template.instance().subscriptionsReady();
},
events: function () {
const instance = Template.instance();
var fc = $('.fc');
return function (start, end, tz, callback) {
//console.log(instance.data.bookings);
//find all, because we've already subscribed to a specific range
var events = Bookings.find().map(function (bk) {
return {
title: bk.serviceName,
start: bk.bookingDate,
editable: bk.bookingDate > new Date(),
allDay: false
};
});
console.log(events);
callback(events);
};
},
onEventClicked: function() {
return function(calEvent, jsEvent, view) {
alert("Event clicked: "+calEvent.title);
}
}
});
Template.Diary.rendered = function () {
const instance = Template.instance();
var fc = this.$('.fc');
this.autorun(function () {
console.log("refetching events");
fc.fullCalendar('refetchEvents');
});
};
The code console.log("refetching events");
is only ever called once, it is this I need to be called whenever the bookings subscription changes.
Diary.html (additional markup removed):
{{#if dataReady}}
{{> fullcalendar
class="fc"
defaultView='agendaWeek'
height="100%"
events=events
eventClick=onEventClicked
}}
{{else}}
<div class="calendar_loading">Loading...</div>
{{/if}}
The actual FullCalendar display is in it's own component which was based from the one packaged on Atmosphere - I'm likely to be customising this quite heavily and also wanted control over the plugin versions hence not using directly. This component is as follows:
fullcalendar.js:
Template.fullcalendar.rendered = function() {
var div = this.$(this.firstNode);
if(this.data != null) {
div.attr('id', this.data.id);
div.addClass(this.data.class);
}
div.fullCalendar(this.data);
};
fullcalendar.html
<template name="fullcalendar">
<div class="fc"></div>
</template>
I think changing your onRendered
method should solve the issue.
Template.Diary.onRendered = function () {
var self = this;
this.autorun(function () {
Bookings.find().fetch();
var fc = self.$('.fc');
fc.fullCalendar('refetchEvents');
});
};
You must have a reactive data source inside your autorun
to ensure that it executes when data changes. In this case we don't actually need to get the data within the autorun
since you are using the FullCalendar events callback. So Bookings.find().fetch()
just ensures each time the collection changes refetchEvents
is run.
Check out this tutorial in case you need some additional help.