2

Requirement: Total an extended prop on 0..n events on every day cell

I understand there are day cell render hooks like day cell content hook among other hooks that we can utilize to accomplish this. The day cell content hook looks very promising, but fails to work when the events are not all in memory and are rather loaded asynchronously because the daycells render before the data is returned. I could not find a good way to work around this short of perhaps rerendering the entire calendar when the data is returned.

Here is some example code that does some of the logic in the case the events ARE IN MEMORY, DOES NOT WORK FOR ASYNCHRONOUS EVENTS (VueJS, but should be similar for Vanilla):

injectCellContent: function(arg) { // this is the day cell content hook
  let CalendarAPI = this.$refs.calendar.getApi();
  let events = CalendarAPI.getEvents();
  let thisDate = moment(arg.date);
  let thisDaysEvents = events.filter(event => moment(event.start).isSame(thisDate, 'day'));
  console.log(thisDaysEvents); // aggregate the values for events on this day

}

Thinking out loud, it almost feels like it would be intuitive to have the days events as part of the argument for the day cell render hook, but I understand they are decoupled from each other.

Question: How can I aggregate values from multiple events for a given day/date?

karns
  • 5,391
  • 8
  • 35
  • 57
  • Are you looking to then display this total within each day cell, or something? – ADyson Jun 08 '22 at 08:21
  • @ADyson - correct. For instance let's say I was displaying invoices on their due dates and each invoice has a "total". How could I total N number of invoices on any given day/daycell? – karns Jun 08 '22 at 11:55
  • ... if all information is in memory, it's fairly trivial using the day cell render hooks. However, as described, the Asynchronous calls make it challenging.. – karns Jun 08 '22 at 11:56
  • Agreed it's tricky. I'd be tempted to implement your event feed as an event source (if you haven't already). You could then handle https://fullcalendar.io/docs/eventSourceSuccess when the event source finishes downloading - that will give you the array of events. You can then filter them by each day, and I'd suggest (because the opportunity to update the day cells directly has passed, really) then adding (to the event array) an all-day event containing the total for that day - you can format it to be as minimal as possible so it looks more like background info than an event. – ADyson Jun 08 '22 at 12:10
  • Of course you could potentially also do the same thing on the server-side when it's compiling the list of events to return to fullCalendar - it might well be more efficient that way. – ADyson Jun 08 '22 at 12:11
  • @ADyson I've actually implemented it successfully using techniques as described in your former comment about registering a success callback. It's a bit clunky, but works. Agreed on potentially processing a response on the back-end. – karns Jun 08 '22 at 12:13
  • ... and I'm embarrassed to post the code since I did not take any time to clean it up :) I will post it I suppose. – karns Jun 08 '22 at 12:14
  • No worries, glad you found a solution – ADyson Jun 08 '22 at 12:23

1 Answers1

2

Although a little messy, the following was how I ended up achieving this. Note that using the day cell render hooks (dayCellContent, I believe), I first added a container to the DOM via injectCellContent function. addEmployeeCounts was called upon the source events refetching successfully via callback. The condition for native events was needed to rerender the counts in the event that a single day was resized or moved, again using callbacks via fullcalendar options.

 getHolderId: function(targetDate) {
   return "batch-calendar__employee-count__" + moment(targetDate, 'yyyy-mm-dd');
 },
 injectCellContent: function(arg) {
   let holder = document.createElement("div");
   holder.className = "batch-calendar__employee-count";
   holder.id = this.getHolderId(arg.date);
   arg.el.append(holder);
 },
 addEmployeeCounts: function(batchEvents, useNativeEvents) {
   let CalendarAPI = this.$refs.calendar.getApi();
   let activeStart = moment(CalendarAPI.view.activeStart);
   let activeEnd = moment(CalendarAPI.view.activeEnd);
   // loop through batchEvents by day and total employee count
   for (var m = moment(activeStart); m.isBefore(activeEnd); m.add(1, 'days')) {
     let holderID = this.getHolderId(m);
     let holder = document.getElementById(holderID);
     holder.innerHTML = "";

     let totalEmployeesForDay = 0;
     if (useNativeEvents) {
       let nativeEvents = CalendarAPI.getEvents();
       let nativeEventsCount = nativeEvents.length;
       for (var i = 0; i < nativeEventsCount; i++) {
         let thisEvent = nativeEvents[i];
         if (moment(m).isBetween(thisEvent.start, thisEvent.end, undefined, '[)')) {
           totalEmployeesForDay += thisEvent.extendedProps.service_order_item.project.employees_needed;
         }
       }
     } else {
       let batchCount = batchEvents.length;
       for (var i = 0; i < batchCount; i++) {
         let thisEvent = batchEvents[i];
         if (moment(m).isBetween(thisEvent.start, thisEvent.end, undefined, '[)')) {
           totalEmployeesForDay += thisEvent.service_order_item.project.employees_needed;
         }
       }

     }
     let holderIcon = document.createElement("span");
     holderIcon.className = "fa fa-user mr-1";
     holder.append(holderIcon)
     holder.append(totalEmployeesForDay);
   }
 },
karns
  • 5,391
  • 8
  • 35
  • 57