0

I pulled source from: https://github.com/markslott/lwc-fullcalendar and it works nicely though I had to make small change at line 132 of the main.js

//return matchesMethod.call(el, selector);
return el.matches(selector);

The issue I am having is that I want to automatically create events for all days that occur in a selection of days. The dateClick works for a single day, but the select event throws an error that says this.calendar is undefined. I don't understand what is missing since the logic is virtually identical between the dateClick and select events other than the select is using a loop...

js code

/* eslint-disable no-console */
import { LightningElement, api, track} from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import fullCalendar from "@salesforce/resourceUrl/fullCalendar";
import { loadStyle, loadScript } from "lightning/platformResourceLoader";
import getEventsNearbyDynamic from "@salesforce/apex/FullCalendarController.getEventsNearbyDynamic";
import { NavigationMixin } from 'lightning/navigation';

//global variables
var objectName;
var startField;
var endField;
var colorField;
var additionalFilter;
var allDayField;
var titleField;

export default class FullCalendarComponent extends NavigationMixin(LightningElement) {
    calendar;
    fullCalendarInitialized = false;

    @api titleField;
    @api objectName;
    @api startField;
    @api endField;
    @api colorField;
    @api additionalFilter;
    @api aspectRatio;
    @api allDayField;
    @api height;

    @api weekView;
    @api dayView;
    @api listView;

    @track calendarLabel;


    connectedCallback() {
        this.addEventListener('fceventclick', this.handleEventClick.bind(this));
        //this.addEventListener('mousewheel', this.handleScroll.bind(this));  
    }

    renderedCallback() {
        if (this.fullCalendarInitialized) {
            return;
        }
        this.fullCalendarInitialized = true;

        //set global vars
        objectName = this.objectName;
        startField = this.startField;
        endField = this.endField;
        colorField = this.colorField;
        additionalFilter = this.additionalFilter;
        allDayField = this.allDayField;
        titleField = this.titleField;

        Promise.all([
            loadScript(this, fullCalendar + "/packages/core/main.js"),
            loadStyle(this, fullCalendar + "/packages/core/main.css")
        ])
        .then(() => {
            //got to load core first, then plugins
            Promise.all([
            loadScript(this, fullCalendar + "/packages/daygrid/main.js"),
            loadStyle(this, fullCalendar + "/packages/daygrid/main.css"),
            loadScript(this, fullCalendar + "/packages/list/main.js"),
            loadStyle(this, fullCalendar + "/packages/list/main.css"),
            loadScript(this, fullCalendar + "/packages/timegrid/main.js"),
            loadStyle(this, fullCalendar + "/packages/timegrid/main.css"),
            loadScript(this, fullCalendar + "/packages/interaction/main.js"),
            loadScript(this, fullCalendar + "/packages/moment/main.js"),
            loadScript(this, fullCalendar + "/packages/moment-timezone/main.js"),
        ]).then(() => {
            console.log("init");
            this.init();
        });
      })
      .catch(error => {
          console.log("error", error);
          this.dispatchEvent(
          new ShowToastEvent({
              title: "Error loading FullCalendar",
              //message: error.message,
              variant: "error"
          })
        );
      });
  }

  init() {
      var calendarEl = this.template.querySelector(".calendar");
      // eslint-disable-next-line no-undef
      this.calendar = new FullCalendar.Calendar(calendarEl, {
      plugins: ["dayGrid", "timeGrid", "list","interaction","moment"],
      views: {
          listDay: { buttonText: "list day" },
          listWeek: { buttonText: "list week" },
          listMonth: { buttonText: "list month" },
          timeGridWeek: { buttonText: "week time" },
          timeGridDay: { buttonText: "day time" },
          dayGridMonth: { buttonText: "month" },
          dayGridWeek: { buttonText: "week" },
          dayGridDay: { buttonText: "day" }
       },
  
       eventClick: info => {
           const selectedEvent = new CustomEvent('fceventclick', { detail: info });
           console.log("eventClick",info);
           this.dispatchEvent(selectedEvent);

           info.event.remove();
       },

       eventMouseEnter: info => {console.log("mouse enter", info)},

       select: function(info) {
           console.log('selected ' + info.startStr + ' to ' + info.endStr);
           let startDate = new Date(info.startStr + 'T00:00:00');
           let endDate = new Date(info.endStr + 'T00:00:00');

    
           for (let d = startDate; d < endDate; d.setDate(d.getDate() + 1)) {
               this.calendar.addEvent({
                    title: 'dynamic event',
                    start: new Date(d),
                    allDay: true
               });
           }
    
       },

       dateClick:info => {
           console.log("date click", info);

           var date = new Date(info.dateStr + 'T00:00:00'); // will be in local time

           this.calendar.addEvent({
               title: 'dynamic event',
               start: date,
               allDay: true
           });
       },

       selectable: true,
       selectMinDistance: 200,
       header: false,
       /* header: {
           left: "title",
           center: "today prev,next",
           right:
      "listDay,listWeek,listMonth,timeGridWeek,timeGridDay,dayGridMonth,dayGridWeek,dayGridDay"
      }, */
      eventSources: [
          {
                events: this.eventSourceHandler,
                id: "custom"
          },
          //{
          //  events: "https://fullcalendar.io/demo-events.json",
          //  id: "demo"
          //}
      ],
    });
    this.calendar.render();
    this.calendarLabel = this.calendar.view.title;
  }

  nextHandler() {
     this.calendar.next();
     this.calendarLabel = this.calendar.view.title;
  }

  previousHandler() {
      this.calendar.prev();
      this.calendarLabel = this.calendar.view.title;
  }

  dailyViewHandler() {
      this.calendar.changeView(this.dayView);
      this.calendarLabel = this.calendar.view.title;
  }

  weeklyViewHandler() {
      this.calendar.changeView(this.weekView);
      this.calendarLabel = this.calendar.view.title;
  }

  monthlyViewHandler() {
      this.calendar.changeView('dayGridMonth');
      this.calendarLabel = this.calendar.view.title;
  }

  listViewHandler() {
      this.calendar.changeView(this.listView);
      this.calendarLabel = this.calendar.view.title;
  }

  today() {
      this.calendar.today();
      this.calendarLabel = this.calendar.view.title;
  }

  refresh() {
      var eventSource = this.calendar.getEventSourceById('custom');
      eventSource.refetch();
  }

  handleScroll(event) {
      console.log("handleScroll");
      event.stopImmediatePropogation();
  }


  handleEventClick(event) {
      let info = event.detail;
      console.log('Event: ' + info.event.title);
      console.log('Coordinates: ' + info.jsEvent.pageX + ',' + info.jsEvent.pageY);
      console.log('View: ' + info.view.type);
      console.log(info);
      this[NavigationMixin.Navigate]({
          type: 'standard__recordPage',
          attributes: {
              recordId: info.event.id,
              actionName: 'view',
          },
      });
      // change the border color just for fun
      //info.el.style.borderColor = 'red';

  }

  eventSourceHandler(info, successCallback, failureCallback) {
      getEventsNearbyDynamic({
          startDate: info.start,
          endDate: info.end,
          objectName: objectName,
          titleField: titleField,
          startField: startField,
          endField: endField,
          colorField: colorField,
          allDayField: allDayField,
          additionalFilter: additionalFilter
      })
      .then(result => {
          if (result) {
              let events = result;
              let e = [];
              for (let event in events) {
                  if (event) {
                      e.push({
                          title: events[event][titleField],
                          start: events[event][startField],
                          end: events[event][endField],
                          Id: events[event].Id,
                          id: events[event].Id,
                          color: events[event][colorField],
                          allDay: events[event][allDayField]
                   });
               }
           }
           console.log("num events = ",e.length);
           successCallback(e);
       }
  })
  .catch(error => {
    console.error("error calling apex controller:",error);
    failureCallback(error);
  });
  }
}

HTML

<template>
    <div class="slds-grid slds-wrap"
        style="background:#ffffff; padding-bottom:0.5rem;padding-left:0.5rem;padding-right:0.5rem;padding-top:0.5rem;overflow-y:auto">
        <div class="slds-col slds-size_8-of-12 slds-col_bump-right">
            <h2 class="slds-text-heading_medium">{calendarLabel}</h2>
        </div>
        <div class="slds-col slds-size_3-of-12">
             <div class="slds-clearfix">
                <div class="slds-float_right">
                    <div class="slds-button-group" role="group">
                        <button class="slds-button slds-button_icon" onclick={previousHandler}>
                            <lightning-icon icon-name="utility:chevronleft" alternative-text="Previous" size="small">
                            </lightning-icon>
                        </button>
                        <button class="slds-button slds-button_icon" onclick={nextHandler}>
                            <lightning-icon icon-name="utility:chevronright" alternative-text="Next" size="small">
                            </lightning-icon>
                        </button>
                        <button class="slds-button slds-button_icon" onclick={refresh}>
                            <lightning-icon icon-name="utility:refresh" alternative-text="Next" size="small">
                            </lightning-icon>
                        </button>
                    </div>
                </div>
            </div>
        </div>
        <div class="slds-col slds-size_3-of-12">
            <div class="slds-button-group" role="group">
                <button class="slds-button slds-button_icon" onclick={today} style="margin-right:1em">
                    <lightning-icon icon-name="standard:today" alternative-text="Today" size="small">
                    </lightning-icon>
                </button>
                <button class="slds-button slds-button_icon" onclick={listViewHandler}>
                    <lightning-icon icon-name="utility:list" alternative-text="List View" size="small">
                    </lightning-icon>
                </button>
                <button class="slds-button slds-button_icon" onclick={dailyViewHandler}>
                    <lightning-icon icon-name="utility:dayview" alternative-text="Day view" size="small">
                    </lightning-icon>
                </button>
                <button class="slds-button slds-button_icon" onclick={weeklyViewHandler}>
                    <lightning-icon icon-name="utility:weeklyview" alternative-text="Weekly View" size="small">
                    </lightning-icon>
                </button>
                <button class="slds-button slds-button_icon" onclick={monthlyViewHandler}>
                    <lightning-icon icon-name="utility:monthlyview" alternative-text="Monthly View" size="small">
                    </lightning-icon>
                </button>
            </div>
        </div>
    </div>
    <div style="background:#ffffff;" class="slds-grid" >
        <div class="calendar" ontouchmove={handleScroll} onscroll={handleScroll}> </div>
    </div>
</template>

meta

<?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="fullCalendarTest">
    <apiVersion>46.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__AppPage,lightning__RecordPage,lightning__HomePage,lightningCommunity__Default">
            <property name="objectName" label="Object name" type="String" required="true" default="Event"></property>
            <property name="startField" label="Start time field name" description="This needs to be a datetime field on the object that indicates the event start time" type="String" required="true" default="StartDateTime"></property>
            <property name="endField" label="End time field name" description="This needs to be a datetime field on the object that indicates the event end time" type="String" required="true" default="EndDateTime"></property>
            <property name="titleField" label="Event title field" type="String" required="true" default="Subject"></property>
            <property name="colorField" label="Color field" description="Any valid CSS value can go here" type="String" default=""></property>
            <property name="allDayField" label="All day event field" description="Field that specifies if the event is an all day event" type="String" default="IsAllDayEvent"></property>
            <property name="additionalFilter" label="Additional Filter" description="starts with AND or OR - end of SOQL statement" type="String" default=""></property>
            <property name="aspectRatio" label="Calendar Aspect Ratio" description="higher values makes the calendar shorter vertically" type="String" default="1.35"></property>
            <property name="height" label="Height of Calendar" description="height of calendar in pixels. unset = automatic" type="Integer" default="650"></property>
            <property name="weekView" label="Full Calendar week view type" type="String" datasource="timeGridWeek,dayGridWeek" default="timeGridWeek" />
            <property name="dayView" label="Full Calendar day view type" type="String" datasource="timeGridDay,dayGridDay" default="timeGridDay" />
            <property name="listView" label="Full Calendar list view type" type="String" datasource="listDay,listWeek,listMonth" default="listMonth" />
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

Apex

public with sharing class FullCalendarController {

    public FullCalendarController() {}

    @AuraEnabled
    public static List<Object> getEventsNearbyDynamic(Date startDate, Date endDate, String objectName, String titleField, String startField, String endField, String colorField, String allDayField, String additionalFilter)  {
        Datetime lastMonth = Datetime.newInstance(startDate.year(),startDate.month(),startDate.day());
        //lastMonth = lastMonth.addMonths(-1);
        String lastMonthStr = lastMonth.formatGmt('yyyy-MM-dd\'T\'hh:mm:ss\'Z\'');
        Datetime nextMonth = Datetime.newInstance(endDate.year(),endDate.month(),endDate.day());
        //nextMonth = nextMonth.addMonths(2).addDays(-1);
        String nextMonthStr = nextMonth.formatGmt('yyyy-MM-dd\'T\'hh:mm:ss\'Z\'');
        String soqlQuery = '';
        if (additionalFilter == null) {
            additionalFilter = '';
        }
        if ((colorField != null) && (colorField != '')) {
            colorField = ','+colorField;
        } else {
            colorField = '';
        }
        if ((allDayField != null) && (allDayField != '')) {
            allDayField = ','+allDayField;
        } else {
             allDayField = '';
        }
        soqlQuery += 'SELECT Id, '+titleField+', '+startField+', '+endField + /*colorField +*/ allDayField+' FROM ' + objectName;
   
        soqlQuery += ' WHERE (('+startField+' >= '+lastMonthStr+' AND '+startField+' <= '+nextMonthStr+')';
        soqlQuery += ' OR ('+endField+' >= '+lastMonthStr+' AND '+endField+' <= '+nextMonthStr+'))';
        soqlQuery += additionalFilter;
        System.debug('startDate = ' + startDate.format());
        System.debug('endDate = ' + endDate.format());
        System.debug('objectName = ' + objectName);
        System.debug('titleField = ' + titleField);
        System.debug('startField = ' + startField);
        System.debug('endField = ' + endField);
        System.debug('colorField = ' + colorField);
        System.debug(soqlQuery);

        List<Object> q = Database.query(soqlQuery);
        if (q == null) {
            q = new List<Object>();
        }
        System.debug('results = ' + q.size());
        return q;
    }

}

1 Answer 1

0

Turns out that while the dateClick recognizes this correction...

this.calendar.addEvent

the select has "this" mapped to the calendar object so changing the call to this...

this.addEvent

resolves the issue. Not sure why the two events treat this differently, but I am not a JS architect so it alludes me.

You must log in to answer this question.

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