DEV Community

loading...
Cover image for Adding a loading state to fullCalendar

Adding a loading state to fullCalendar

Brad Goldsmith
Bartender turned web developer, bootcamp / self taught and proud. Currently working as a laravel developer using TALL stack with the occasional bit of Vue thrown in the mix.
・3 min read

So last week I wrote my first "blog" and it was about the month change event. This week I tackled a simple yet effective project which was to add a loading state to calendar whenever an async call is made. Why is this important? Well the overall call to get all events for a certain date range takes roughly 200miliseconds, which I know doesn't seem like but the idea is (since we are using this on mobile) is to make sure you don't have the ability to click on dates twice before an event is created and just to give a sense of hey something is happening here.

Ideally when a single date is click if we could add a loading wheel to that particular date, well that would be rad, and that would be more work that I really want to deal with. So in this project fullCalendar is being loaded through a Vue component and we are using Tailwind css. Little side note at first I thought tailwind was tailwind was stupid and way to verbose in the html, not I love it and will use it everywhere in the future. If you haven't given it a spin do yourself a favor and check it out.

So my idea for a loading state is to basically make a super quick change to the overall style of the calendar and make it un-clickable while that style is present. I know what an amazing loading state, right?!?!?!?!?!?!? Well working on small team we have limited bandwidth and this seemed like the easiest win. Since we are using Vue / tailwind seems like I could create a simple v-if for a div and that div would be an overlay of the calendar with an opacity and z-index.

  data() {
    return {
      selectedCharter: null,
      availableCharters: [],
      isLoading: false,
      calendarStart: "",
      calendarEnd: "",
      calendarOptions: {
        timeZone: "UTC",
        nextDayThreshold: "00:00:00",
        plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
        events: [],
        editable: true,
        eventDurationEditable: false,
        eventDrop: this.handleEventDrop,
        dateClick: this.handleDateClick,
        eventResize: this.handleEventResize,
        datesSet: this.handleMonthChange,
        headerToolbar: {
          right: "today,prev,next",
          left: "title",
        },
      },
    };
  },
Enter fullscreen mode Exit fullscreen mode

So that's my data return object on the component and as you can see I added the isLoading property and set it to false to being with.

    <div class="relative">
      <div>
        <FullCalendar ref="fullCalendar" :options="calendarOptions">
          <template v-slot:eventContent="event">
            <RangeEvent :event="event" @eventDeleted="handleEventDeleted" />
          </template>
        </FullCalendar>
      </div>
      <div
        v-if="isLoading"
        class="absolute top-0 right-0 h-full w-full bg-white opacity-50 z-50"
      ></div>
    </div>
Enter fullscreen mode Exit fullscreen mode

And here is what I did in the template of the Vue file added a wrapper div that has a position relative class and then below the FullCalendar component I have my loading state div. Super simple conditional that makes and "overlay" on the calendar. Super simple yet super effective.

So in my code there are 2 places I need set isLoading to true. Whenever the month is changed, and whenever a new event is added to the calendar. Then to make sure that the loading state is not on there forever whenever I fetchEvents (which is called at both places where I set isLoading to true), I set it to false at the end of the async call.

So there you have it a super effective yet simple loading state. This calendar has kind of been my project now for the past few weeks and I must say the other day we got some unsolicited feedback from a few users saying they really like these new upgrades and that it's more intuitive than our main competitors. Funny how a few simple non super devy things can make the difference.

Discussion (0)