13

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?

  • Share the component code as well on how you are invoking these functions? – Mohith Shrivastava Mar 24 '16 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 Mar 29 '16 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. – prashanthkr Mar 29 '16 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 Mar 29 '16 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 Apr 4 '16 at 6:01
14

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
            });
        },
})
  • one thing i see missing is the moments.js file in the ltng:require scripts. – Rao Jun 12 '17 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 Jun 12 '17 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 Jun 12 '17 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 Aug 27 '18 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 Aug 29 '18 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

  • I tried that too. Didn't work:( – prashanthkr Mar 29 '16 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({...});
 }
  • I actually already had that in my code. I edited the question to include the state check. Doesn't work:( – prashanthkr Mar 30 '16 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}"  />
  • 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. – prashanthkr Apr 1 '16 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. – sfdcJameson Apr 1 '16 at 20:56

protected by Community Apr 12 '18 at 14:03

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.