Search code examples
meteormeteor-blaze

Meteor update JQuery plugin data


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>

Solution

  • 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.