[SalesForce] FullCalendar Lightning Component Issue – TypeError: undefined is not an object (evaluating ‘moment.fn’)

I'm implementing a Lightning Component with FullCalendar.io

I'm using FullCalendar 2.7.1
https://github.com/fullcalendar/fullcalendar/releases/tag/v2.7.1

My static resources are

  • jQuery: jquery.min.js (v2.1.4)
  • FullCalendarJS: fullcalendar.js (v2.7.1)
  • FullCalendarCSS: fullcalendar.css (v2.7.1)

Error

TypeError: undefined is not an object (evaluating 'moment.fn') throws at https://org-dev-ed.lightning.force.com/c/calendarHarness.app:1043:28

calendarHarness.App

<aura:application >
  <c:fullCalendar />
</aura:application>

fullCalendar.cmp

<aura:component controller="FullCalendarController" implements="force:appHostable">
  <ltng:require scripts="/resource/1484095596000/jQuery,/resource/1484095745000/FullCalendarJS" afterScriptsLoaded="{!c.afterScriptsLoaded}"/>
  <ltng:require styles="/resource/1484095772000/FullCalendarCSS"/>
  <aura:attribute name="events" type="Booking__c[]" />
  <div id="calendar"></div>
</aura:component>

fullCalendarController.js

({
  afterScriptsLoaded: function(cmp,evt,helper){
    var events = cmp.get("v.events");
    console.log(events);
    if(!events.length)
    {
        helper.fetchEvents(cmp);
    }
  }
})

fullCalendarHelper.js

({
loadDataToCalendar :function(component,data){      
    var ele = component.find('calendar').getElement();
        $(ele).fullCalendar({
            header: {
                left: 'prev,next today',
                center: 'title',
                right: 'month,basicWeek,basicDay'
            },
            defaultDate: '2016-04-01',
            editable: true,
            eventLimit: true,
            events:data
    });
},

tranformToFullCalendarFormat : function(component,events) {
    var eventArr = [];
    for(var i = 0;i < events.length;i++){
        eventArr.push({
            'id':events[i].Id,
            'start':events[i].StartDateTime,
            'end':events[i].EndDateTime,
            'title':events[i].Subject
        });
    }
    return eventArr;
},

fetchEvents : function(component) {
    var action = component.get("c.getEvents"); 
    var self = this;
    action.setCallback(this, function(response) {
        var state = response.getState();
        if(component.isValid() && state === "SUCCESS"){
            var eventArr = self.tranformToFullCalendarFormat(component,response.getReturnValue());
            self.loadDataToCalendar(component,eventArr);
            component.set("v.events",eventArr);
        }
    });

    $A.enqueueAction(action); 
} 
})

fullCalendarController.apxc

public class FullCalendarController {
  @AuraEnabled
  public static List<Booking__c> getEvents(){
    return [SELECT ID,Name,Event_Start__c,Event_End__c FROM Booking__c];
  }
}

—– Update —–
fullCalendar.cmp

<aura:component controller="FullCalendarController" implements="force:appHostable">
  <ltng:require
    styles="{!$Resource.FullCalendarCSS + '/fullcalendar.css'}"
    scripts="{!join(',', 
      $Resource.FullCalendarJS + '/fullcalendar.js', 
      $Resource.jQuery + '/jquery.min.js',
      $Resource.MomentJS + '/moment.min.js')}"
    afterScriptsLoaded="{!c.scriptsLoaded}"/>
  <aura:attribute name="events" type="Booking__c[]" />
  <div id="calendar"></div>
</aura:component>

Best Answer

It's probably best to avoid any external dependencies (moment.js).

However if you can't avoid it, you will need to include the moment.js script in your lightning require call. Also, I'm pretty sure that you need to append all your requires together into one.

Here is how to use lightning require:

<ltng:require
    scripts="{!join(',', 
    $Resource.jsLibraries + '/jsLibOne.js', 
    $Resource.jsLibraries + '/jsLibTwo.js')}"/>

Reference here