15

I have a lightning component that pulls data from Salesforce and adds them as events on a calendar created using fullcalendar. The component helper contains the method fetching the data from Server. The component controller first pulls the data from Server and then creates the fullCalendar. This is the general structure of the code:

    calendar.cmp
    --------------------
    <aura:component...>
            <ltng:require scripts="/resource/jQuery,/resource/FullCalendar/js/fullcalendar.min.js"  afterScriptsLoaded="{!c.afterScriptsLoaded}"/>
    </aura>
    calendarController.js
    ---------------------
    afterScriptsLoaded: function(cmp,evt,helper){
     var events = cmp.get("v.events");
     if(events == null)
      {
         helper.fetchEvents(cmp);
      }
      helper.createCalendar(cmp,events);
    }

     calendarHelper.js
     ---------------------
     fetchEvents: function(cmp){
        var action = cmp.get("c.fetchFromServer");
        action.setCallback(this, function(response){ 
            var state = response.getState();
            if(cmp.isValid() && state === "SUCCESS"){
                cmp.set("v.events",response.getReturnValue());
            }
        });
     },
     createCalendar: function(cmp,events){
        $('#calendar').fullCalendar({...});
     }

I added the component to a lightning tab for testing. The first time I visit the tab, the component loads. But when I visit another tab and come back to this tab, the component is blank, even though the controller and helper methods are invoked in correct sequence and the data is pulled from the server too. This is because the calendar div is not visible yet when FullCalendar attempts to draw the calendar. I tried calling .fullCalendar('render') in rerender,afterRender method too, but that didn't help. However, when I reload the tab, the calendar is rendered correctly.

How can I ensure the data is rendered every time the calendar is accessed?

6
  • Share the component code as well on how you are invoking these functions? Commented Mar 24, 2016 at 0:06
  • prashanthkr.... use setTimeout($('#calendar').fullCalendar();, 3000);.... try with settimeout.. to insure your all resource files load then after full calendar initialize
    – Ratan Paul
    Commented Mar 29, 2016 at 9:03
  • @sfdcweb the calendar is loaded and i can find it in the dev console on chrome. Its just that the data that is fetched from the server doesn't render on the UI. Commented Mar 29, 2016 at 17:29
  • prashanthkr.... then you need to wait untill fullCalendar properly loades.. you need to just increase set time out value.. I suggest first try 10,000( 10 second) after that try to reduce the value and use minimum value
    – Ratan Paul
    Commented Mar 29, 2016 at 17:38
  • Try calling createCalendar after setting the v.events.Since callback registered in action.setCallback() get executed asynchronously by aura framework,once the data is received from server.So,there's a less likely there will be values in when u do cmp.get('v.events') and give to fullcalendar in createCalendar
    – Praveen
    Commented Apr 4, 2016 at 6:01

5 Answers 5

15

Hi @prashanthkr based on my comment i tried to prototype this, and it works like charm during inital load/refresh in both SF1 and desktop.

Edit on 20-09-2017:

Fullcalendar works for the below js version with LockerService(v40) in place :

jQuery - v3.2.1

MomentJS - v2.18.1

FullCalendar - v3.1.0/v3.5.1(latest)

NOTE: Libraries should be included in the above order.

Here's my code:

fullCalendar.cmp

<aura:component controller="FullCalendarController" implements="force:appHostable">
    <ltng:require styles="{!$Resource.FullCalenderJs     + '/fullcalendar/fullcalendar.css'}"
          scripts="{!join(',',
                       $Resource.FullCalenderJs  + '/fullcalendar/lib/jquery.min.js',
                       $Resource.FullCalenderJs  + '/fullcalendar/lib/moment.min.js',
                       $Resource.FullCalenderJs  + '/fullcalendar/fullcalendar.js'
                      )}"
          afterScriptsLoaded="{!c.afterScriptsLoaded}" />
    <ltng:require styles="/resource/FullCalendar/FullCalendar/fullcalendar.css"/>
    <aura:attribute name="events" type="Object[]" />
    <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(data){        
        $('#calendar').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); 
    }, 
})

Apex Controller:(FullCalendarController)

public class FullCalendarController {

    @AuraEnabled
    public static List<Event> getEvents(){
        return [SELECT AccountId,EndDateTime,Id,StartDateTime,Subject FROM Event];
    }
}

-- UPDATE --

Above solution work properly in Standalone app not in LEX on subsequent loads. After hours of inspecting DOM Element returned by jquery selector which was different compared to the first time load,jquery was not returning the proper DOM when the component descriptor is loaded from cache.

So Inorder to get the proper DOM,on every load,i replaced the jquery selector $('#calendar') to component.find('#calendar').getElement() and it actually returns the proper DOM Element,and the fullcalendar is getting rendered on subsequent loads in LEX.

fullCalendar.cmp should be like this to support above:

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

And loadDataToCalendar method should look like this:

({
 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
            });
        },
})
6
  • one thing i see missing is the moments.js file in the ltng:require scripts.
    – Rao
    Commented Jun 12, 2017 at 18:00
  • @Rao At the time of writing the answer code worked without moment.js. I need to check with the current version and will update the answer accordingly.
    – Praveen
    Commented Jun 12, 2017 at 18:08
  • yes please, I just tried and get a cannot read fn property. I added the moment.js and the calendar worked as expected.
    – Rao
    Commented Jun 12, 2017 at 18:20
  • Hey Praveen - I got my calendar working with a custom object, however, I can't get the calendar to rerender. Are you still active here? If so, I'll explain further :)
    – erikvm
    Commented Aug 27, 2018 at 13:23
  • @erikvm Please create a new question and link that to this question. So that we can help you to sort out the issue.
    – Praveen
    Commented Aug 29, 2018 at 6:59
0

I guess this is a timing problem between the static resource loading and the rendering lifecycle auf Lightning Components. You can try to instantiate your component from a renderer (part of the component bundle which is then named calendarRenderer.js) by doing the following:

({
    afterRender : function(component, helper) {
        this.superAfterRender();
        helper.createCalendar()
    }
})

You can find more information about renderer here: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_renderers.htm

Hope that helps

Cheers
Chris

1
  • I tried that too. Didn't work:( Commented Mar 29, 2016 at 17:11
0

I also face the same issue. Check the stage of the Server side controller before set the response for component.

try this code :-

fetchEvents: function(cmp){
    var action = cmp.get("c.fetchFromServer");
    action.setCallback(this, function(response){ 
    var state = response.getState();
            if (state === "SUCCESS") {
               cmp.set("v.events",response.getReturnValue());
            }
    });
 },
 createCalendar: function(cmp,events){
    $('#calendar').fullCalendar({...});
 }
1
  • I actually already had that in my code. I edited the question to include the state check. Doesn't work:( Commented Mar 30, 2016 at 23:07
0

You need to include additional scripts, here's what I'm using:

    <link href="{!$Resource.FullCalendarCSS}" rel="stylesheet" />
    <link href="{!$Resource.FullCalendarPrintCSS}" rel="stylesheet" media="print" />

    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
    <script src="{!$Resource.fullCalendarMinJS}"></script>
    <apex:includeScript value="{!$Resource.MomentMinJS}"  />
2
  • no, all the scripts are included already. Otherwise the calendar wouldn't render when I refresh the page. I skipped them in the question for brevity. Commented Apr 1, 2016 at 20:50
  • Ok, gotcha. Sorry, skipped through the last part of the question, didn't see the part where you said it was loading after the refresh. Commented Apr 1, 2016 at 20:56
0

I know it is an old post, but i followed the best answer here but the calendar doesn't render in my experience page, any ideas my code is the same as best answer with the LEX amendment in it the console log doesn't show any errors, included debug and console log show success Apex returns the expected event amount

In the builder i get the initial error Error in $A.getCallback() [Cannot read properties of undefined (reading 'getElement')] Callback failed: apex://FullCalendarController/ACTION$getEvents Failing descriptor:

any ideas? The page simply doesn't show anything

thanks

1
  • okay the issue was the LEX amendment, i returned it to the initial code and it displays loadDataToCalendar :function(data){ $('#calendar').fullCalendar({ header: { left: 'prev,next today', center: 'title', right: 'month,basicWeek,basicDay' }, defaultDate: '2016-04-01', editable: true, eventLimit: true, events:data }); },
    – Moggy
    Commented Sep 21, 2023 at 9:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .