How to use FullCalendar plugin with Typescript

Calendars are integral part of majority of applications. There are always scheduled events, meetings, sales etc. that every application has to keep track of and show to users of the applications. In this post I will show some sample code that demonstrates how to use Full Calendar using Typescript in ASP.Net MVC application. This implementation is not just specific to Visual Studio. You can use the same Typescript implementation in other platforms as well.

fullcalendar using typescript

Pre-requisites

Without going into lot of details, I will simply list what all components you will need in your ASP.Net MVC application. Depending on platform you use, the steps may be little different different but the basic requirements stay the same.

  • FullCalendar: You can use install NuGet package. The issue with this is that NuGet package is old. It is at version 2.4. You can goto FullCalendar website and download latest and greatest vesion. At this time of writing it is at version 2.8. You can first install V2.4 using NuGet and then manually replace required Javascript and CSS files in folders with the latest downloaded version.
  • Typings: - Get fullCalendar.TypeScript.DefinitelyTyped Typescript typings file using NuGet package manager.

Implementation

For your MVC project, create bundles that will include required javascript and css files. In my sample I have following code in my BundleConfig.cs file.

    bundles.Add(new StyleBundle("~/Content/calendar").
        Include("~/Content/fullcalendar.css", "~/Content/fullcalendar-print.css"));
    bundles.Add(new ScriptBundle("~/bundles/calendar").
        Include("~/Scripts/moment.js","~/Scripts/gcal.js", "~/scripts/fullcalendar.js"));

Next you will need a placeholder HTML element in your page where this calendar is goig to be placed. Following sample shows how HTML in prototype page looks like.

    <div class="row">
        <div class="col-sm-6"><div id="gragesale_calendar"></div></div>
    </div>

You will need some sort of data source that is going to populate the calendar entries. I have implemented a Webapi that returns collection of objects containing data required to render calendar entries. Following code from prototype WebApi controller that returns some mock data.

    namespace ByteScript.Controllers.Api
    {
    [RoutePrefix("api/Events")]
    public class EventsController : ApiController
    {
        [HttpGet]
        [Route("GarageSales/{startDate:double?}/{endDate:double?}")]
        public IList<GarageSaleModel> GetGrageSales(double? startDate, double? endDate)
        {
            var start = DateTime.Now;
            if (startDate.HasValue)
            {
                start = ConvertUnixTimestamp(startDate.Value);
            }
            var sales = new List<GarageSaleModel>
            {
                new GarageSaleModel {Id = 1, Title = "G Sale 1",
                    StartTime = ConvertToUnixTimestamp(start.AddDays(1)),
                    EndTime = ConvertToUnixTimestamp(start.AddDays(1.5))},
                new GarageSaleModel {Id = 2, Title = "G Sale 2",
                    StartTime = ConvertToUnixTimestamp(start.AddDays(3)),
                    EndTime = ConvertToUnixTimestamp(start.AddDays(3.5))},
                new GarageSaleModel {Id = 3, Title = "G Sale 3",
                    StartTime = ConvertToUnixTimestamp(start.AddDays(6)),
                    EndTime = ConvertToUnixTimestamp(start.AddDays(6.5))}
            };
            return sales;
        }

        private static DateTime ConvertUnixTimestamp(double timeStamp)
        {
            return DateTimeOffset.FromUnixTimeMilliseconds((long)timeStamp).DateTime;
        }

        private static double ConvertToUnixTimestamp(DateTime time)
        {
            return new DateTimeOffset(time).ToUnixTimeMilliseconds();
        }
    }
}

Following snippet shows how Typescript is implemented to use FullCalendar plugin.

Portal.DataService.ts

    interface ICalendarEvent {
        Id:number,
        Title: string;
        StartTime: number;
        EndTime: number;
        Address:string;
    }

Portal.Calendar.ts

    /// <reference path="../typings/jquery/jquery.d.ts"/>
    /// <reference path="../typings/fullCalendar/fullCalendar.d.ts"/>
class PortalCalendar {
    calendarElem:JQuery;
    calendar: JQuery;
    timerHandler: number;
    refreshTimeout: number;
    dataUrl: string;
    startDate: moment.Moment;
    endDate: moment.Moment;
    timeZone: string | boolean;
    renderedEvents:Array<ICalendarEvent>;

    constructor(elemSelector: string) {
        this.calendarElem = $(elemSelector);
        this.initialize();
    }

    private initialize() {
        var self = this;
        self.renderedEvents = [];
        // Initialize a timer to refresh calendar data.
        self.dataUrl = PortalSettings.baseUrl + "api/events/GarageSales/";
        self.refreshTimeout = 5000;
        self.timerHandler = 0;
        this.calendar = this.calendarElem.fullCalendar({
            header: {
                right: "next",
                center: "title",
                left: "prev"
            },
            defaultDate: moment(),
            editable: true,
            theme: false,
            eventSources: [
                {
                    events: (start: moment.Moment, end: moment.Moment, timeZone: string | boolean, callBack: any) => {
                        self.startDate = start;
                        self.endDate = end;
                        self.timeZone = timeZone;
                        self.getCalendarEventsData(self, self.startDate, self.endDate, self.timeZone, callBack);
                    }
                }
            ]
        });
    }

    private createDataRefreshTimer() {
        this.timerHandler = setTimeout(
            () => {
                this.calendarElem.fullCalendar("refetchEvents");
            },
            this.refreshTimeout);
    }

    private clearDataRefreshTimer() {
        if (this.timerHandler !== 0) {
            clearTimeout(this.timerHandler);
            this.timerHandler = 0;
        }
    }

    private getCalendarEventsData(thisCalendar:PortalCalendar, 
       start:moment.Moment, end:moment.Moment, timezone:string | boolean, callback:any) {
        thisCalendar.clearDataRefreshTimer();
        let url = thisCalendar.dataUrl;
        if (start != null) {
            url += start;
            if (end != null) {
                url += `/${end}`;
            }
        } else {
            url += moment().valueOf();
        }
        $.ajax({
            url:url
        }).done((data) => {
            callback(thisCalendar.createCalendarEvents(data));
        }).always(() => {
            // Wait for call to complete before firing next one.
            thisCalendar.createDataRefreshTimer();
        }).fail(() => {
            if (console && console.log) {
                console.log("Unable to get calendar data!");
            }
        });
    }

    private createCalendarEvents(events: Array<ICalendarEvent>): Array<FullCalendar.EventObject> {
        const calendarEvents: Array<FullCalendar.EventObject> = [];
        for (let calendarEvent of events) {
            const event: FullCalendar.EventObject = {
                id:calendarEvent.Id,
                title: calendarEvent.Title,
                start: moment(calendarEvent.StartTime),
                end: moment(calendarEvent.EndTime)
            };
            calendarEvents.push(event);
        }
        return calendarEvents;
    }
}

Auto refresh Calendar Entries

Key to a dynamic calendar is that you will need a way to refresh calendar entries as and when new events popup in your application. In this prototype I have added a timer that fires every 5 seconds. This timer triggers refetchEvents method on Fullcalendar object. This method triggers refetch of calendar data for even sources that are assigned to calendar object. In this implementation I have assigned a function that is triggered to get data.

This demonstrates a very simple and minimum Typescript implementation showing how to use FullCalendar jQuery plugin in your MVC application.

comments powered by Disqus

Search

Social

Weather

Monthly Posts