FullCalendar using a JavaScript remoting callback

FullCalendar quick start illustrates a simple way to get FullCalendar working in a Visualforce page. All the event data was just emitted as JSON text in the generated page. But FullCalendar is typically used where the date range being displayed can be changed making that approach impractical. Two alternate ways to supply the event data are supported:

  • via a json feed – a GET url is configured
  • via a function – an arbitrary piece of JavaScript is configured

Both of these are passed the date range that is required for the calendar so that only the relevant events can be returned. The second of these provides more opportunity to do processing on the client side and is the approach described below.

Here is a simplified version of the Apex controller. The Event class models the values required for each event by FullCalendar and Apex automatically serializes the returned list of these as JSON because of the RemoteAction annotation. The arguments to the events method are two Apex Date primitives and an SObject (whose fields are displayed in the page and provide additional control over the calendar content):

public with sharing class CalendarController {
    
    public class Event {
    
        public String title;
        public String tip;
        public DateTime starts;
        public DateTime ends;
        
        public Event(String title, String tip, DateTime starts, DateTime ends) {
            this.title = title;
            this.tip = tip;
            this.starts = starts;
            this.ends = ends;
        }
    }

    // Page calls back into this
    @RemoteAction
    public static Event[] events(Date starts, Date ends, Filter__c filter) {

        System.debug('>>> starts=' + starts + ' ends=' + ends + ' filter=' + filter);

        List<Event> events = new List<Event>();

        // Loop goes here here where query results are turned into Event objects

        return events;
    }
}

Here is the change to what is assigned to “events” in the Visualforce page. Each time FullCalendar requires event data it invokes this JavaScript function. The function obtains values from the SObject fields in the page and passes those together with the start and end dates to the controller via the standard remote action call that automatically serializes the request as JSON. The returned results are passed into FullCalendar by invoking the callback function supplied in the original function call:

events: function(start, end, callback) {
    
    // JQuery references to the hidden id fields for lookups
    // and also to checkbox fields assigned to fields whose
    // names match the Filter__c SObject
    var filter = {
        Booking__c: bookingLkid.val(),
        Class__c: clazzLkid.val(),
        ShowAvailable__c: showAvailable.prop('checked'),
        ShowBooked__c: showBooked.prop('checked')
    };
    
    // JavaScript dates need formatting to be accepted at the Apex side
    Visualforce.remoting.Manager.invokeAction(
        '{!$RemoteAction.CalendarController.events}',
        start.toUTCString(),
        end.toUTCString(),
        filter, 
        function(result, event) {
            if (event.status) {
                // Jobs done here:
                // 1) In Apex "end" is a reserved word so using "ends"; change back
                // 2) The Apex DateTime is serialized as milliseconds; change to Date
                // 3) The HTML < and > are replaced by entities; turn back
                for (var i = 0; i < result.length; i++) {
                    var r = result[i];
                    r.start = new Date(r.starts);
                    r.end = new Date(r.ends);
                    r.tip = r.tip.replace(/&lt\;/g, '<').replace(/&gt\;/g, '>');
                }
                // Invoke FullCalendar function
                callback(result);
            } else {
                alert('ERROR:\n' + event.message + '\n' + event.where);
            }
        });
}

Gotchas along the way were (API 25.0):

  • Although the remote class and method being invoked are explicit in the call, if the class isn’t the controller or an extension, a page build error of “No remoted actions found to resolve…” results.
  • The automatic serialization of Date appears broken in both directions: see the JavaScript above for work-arounds; there may be similar issues with other types.
  • As the JSON serialization can’t be configured its awkward to generate JSON that includes any Apex reserved words.
  • The “tip” string is HTML generated by the Apex code and so needed some manipulation on the client-side to remove the encoding introduced by the serialization.
  • The Force.com documentation is short on details and examples so expect to spend time debugging to figure out what is actually happening.

Finally I’d like to mention that adding a tooltip to FullCalendar took less than 5 minutes and worked first time: all that was needed was the qTip JavaScript and this small addition:

eventRender: function(event, element) {
    element.qtip({
        content: event.tip
    });
}