4

I try to use FullCalendar in Blazor webassembly page. Anyone has idea how to use FullCalendar in Blazor ??? Anyone can help me ?

I include FullCalendar with libman.json

   {
      "library": "@fullcalendar/core",
      "provider": "unpkg",
      "destination": "wwwroot/lib/fullCalendar"
    }

In wwwroot/index.html

   <script src="_framework/blazor.webassembly.js"></script>
   <script src="exampleJsInterop.js"></script>

My js File "exampleJsInterop.js" in wwwroot folder

window.exampleJsFunctions = {

    showPrompt: function (text) {
        return prompt(text, 'Type your name here');
    },
    displayWelcome: function (welcomeMessage) {
        document.getElementById('welcome').innerText = welcomeMessage;
    },

    calendar: function () {
        document.getElementById("#calendar").fullCalendar({
            schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
            editable: false,
            allDaySlot: true,
            eventLimit: true,
            axisFormat: 'HH:mm',
            slotLabelFormat: 'HH:mm',
            aspectRatio: 2,
            header: {
                left: 'today prev,next',
                center: 'title',
                right: 'month '
            },
            //defaultView: 'timelineDay',
            selectable: true,
        });
    }
    
};

The Razor page

@page "/fullCalendar"
@inject IJSRuntime JsRuntime;

<h1>FullCalendar</h1>

<button type="button" class="btn btn-primary" @onclick="test">
    Bouton test
</button>

<div class="container">
    <div id='calendar'></div>
</div>

@code {
      private async void test() {
        await JsRuntime.InvokeVoidAsync("exampleJsFunctions.calendar");
    }
}

What's wrong with the code ?

The error :

(Microsoft.JSInterop.JSException: $ is not** defined)   
>main.js:1 
Failed to load resource: the server responded with a status of 404 (Not Found)
    blazor.webassembly.js:1 Debugging hotkey: Shift+Alt+D (when application has focus)
    favicon.ico:1 Failed to load resource: the server responded with a status of 404 (Not Found)
    blazor.webassembly.js:1 WASM: 
    d.printErr @ blazor.webassembly.js:1
    put_char @ mono.js:1
    write @ mono.js:1
    write @ mono.js:1
    doWritev @ mono.js:1
    ___syscall146 @ mono.js:1
    Module._mono_background_exec @ mono.js:1
    pump_message @ mono.js:1
    setTimeout (async)
    _schedule_background_exec @ mono.js:1
    Module._mono_wasm_invoke_method @ mono.js:1
    callMethod @ blazor.webassembly.js:1
    beginInvokeDotNetFromJS @ blazor.webassembly.js:1
    l @ blazor.webassembly.js:1
    e.invokeMethodAsync @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    t.dispatchEvent @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    e.onGlobalEvent @ blazor.webassembly.js:1
    Show 26 more frames
    blazor.webassembly.js:1 WASM: Unhandled Exception:
    d.printErr @ blazor.webassembly.js:1
    put_char @ mono.js:1
    write @ mono.js:1
    write @ mono.js:1
    doWritev @ mono.js:1
    ___syscall146 @ mono.js:1
    Module._mono_background_exec @ mono.js:1
    pump_message @ mono.js:1
    setTimeout (async)
    _schedule_background_exec @ mono.js:1
    Module._mono_wasm_invoke_method @ mono.js:1
    callMethod @ blazor.webassembly.js:1
    beginInvokeDotNetFromJS @ blazor.webassembly.js:1
    l @ blazor.webassembly.js:1
    e.invokeMethodAsync @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    t.dispatchEvent @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    e.onGlobalEvent @ blazor.webassembly.js:1
    Show 26 more frames
    **blazor.webassembly.js:1 WASM: Microsoft.JSInterop.JSException: $ is not** defined
    d.printErr @ blazor.webassembly.js:1
    put_char @ mono.js:1
    write @ mono.js:1
    write @ mono.js:1
    doWritev @ mono.js:1
    ___syscall146 @ mono.js:1
    Module._mono_background_exec @ mono.js:1
    pump_message @ mono.js:1
    setTimeout (async)
    _schedule_background_exec @ mono.js:1
    Module._mono_wasm_invoke_method @ mono.js:1
    callMethod @ blazor.webassembly.js:1
    beginInvokeDotNetFromJS @ blazor.webassembly.js:1
    l @ blazor.webassembly.js:1
    e.invokeMethodAsync @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    t.dispatchEvent @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    e.onGlobalEvent @ blazor.webassembly.js:1
    Show 26 more frames
    blazor.webassembly.js:1 WASM: ReferenceError: $ is not defined
    d.printErr @ blazor.webassembly.js:1
    put_char @ mono.js:1
    write @ mono.js:1
    write @ mono.js:1
    doWritev @ mono.js:1
    ___syscall146 @ mono.js:1
    Module._mono_background_exec @ mono.js:1
    pump_message @ mono.js:1
    setTimeout (async)
    _schedule_background_exec @ mono.js:1
    Module._mono_wasm_invoke_method @ mono.js:1
    callMethod @ blazor.webassembly.js:1
    beginInvokeDotNetFromJS @ blazor.webassembly.js:1
    l @ blazor.webassembly.js:1
    e.invokeMethodAsync @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    t.dispatchEvent @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    (anonymous) @ blazor.webassembly.js:1
    e.onGlobalEvent @ blazor.webassembly.js:1
    Show 26 more frames
    blazor.webassembly.js:1 WASM:     at testDan (http://localhost:53863/:30:17)
    d.printErr @ blazor.webassembly.js:1
    put_char @ mono.js:1
    write @ mono.js:1
    write @ mono.js:1
    doWritev @ mono.js:1
    ___syscall146 @ mono.js:1
    Module._mono_background_exec @ mono.js:1
    pump_message @ mono.js:1
    setTimeout (async)
    _schedule_background_exec @ mono.js:1
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Daniel
  • 61
  • 5
  • 2
    It would help a lot if you tell us what happens when you execute the code. An error? Some unexpected behaviour? If you're clear about the actual problem then it usually makes it easier to diagnose – ADyson Oct 14 '19 at 21:41
  • 1
    Whuch version of fullCalendar are you trying to use exactly? The error makes me think maybe it's expecting jQuery to be available, although at a brief glance through the code I can't actually see jQuery being used. Perhaps it's used elsewhere in your JavaScript, if you've got any more on that page? – ADyson Oct 15 '19 at 13:07
  • P.s. whichever version you're using, document.getElementById("#calendar") is not the correct way to initialise the calendar. Check the fullCalendar documentation, which contains examples for all versions – ADyson Oct 15 '19 at 13:08

1 Answers1

1

After a week of work I managed to get FullCalendar version 4 running in blazor net5.

CalendarOptions.cs

using System;
using System.Collections.Generic;

namespace SmartApp.Components
{
    public class CalendarOptions
    {
        public string Id { get; set; }
        public string DefaultView { get; set; } = CalendarView.ListWeek;
        public DateTime DefaultDate { get; set; } = DateTime.Now;
        public List<CalendarEvent> CalendarEvents { get; set; } = new List<CalendarEvent>();
    }

    public class CalendarEvent
    {
        public string Identifier { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public DateTime Start { get; set; }
        public DateTime? End { get; set; }
        public string Url { get; set; } = string.Empty;
        public string SolidColor { get; set; }
        public string DotColor { get; set; }

        public string ClassName => $"{(string.IsNullOrEmpty(SolidColor) ? "" : $"fc-event-solid-{SolidColor}")} " +
                                   $"{(string.IsNullOrEmpty(DotColor) ? "" : $"fc-event-{DotColor}")}";

        public CalendarEventStatus Status { get; set; }
    }

    public class CalendarView
    {
        public static string DayGridMonth = "dayGridMonth";
        public static string TimeGridWeek = "timeGridWeek";
        public static string TimeGridDay = "timeGridDay";
        public static string ListWeek = "listWeek";
    }

    public class CalendarEventSolidColor
    {
        public static string Primary = "primary";
        public static string Warning = "warning";
        public static string Error = "error";
        public static string Success = "success";
        public static string Info = "info";
    }

    public class CalendarEventDotColor
    {
        public static string Primary = "primary";
        public static string Warning = "warning";
        public static string Error = "error";
        public static string Success = "success";
        public static string Info = "info";
        public static string Light = "light";
    }

    public enum CalendarEventStatus
    {
        Deleted = 0,
        Active = 1,
        Changed = 2
    }
}

InputCalendar.razor This is a reusable component

@namespace SmartApp.Components

@inject IJSRuntime JSRuntime

<div id="@Id"></div>

@code {
    string _id;
    [Parameter]
    public string Id
    {
        get => _id ?? $"Calendar_{_uid}";
        set => _id = value;
    }

    [Parameter]
    public List<CalendarEvent> Events { get; set; }

    [Parameter]
    public string View { get; set; } = CalendarView.ListWeek;

    [Parameter]
    public EventCallback<List<CalendarEvent>> EventsChanged { get; set; }


    readonly string _uid = Guid.NewGuid().ToString().ToLower().Replace("-", "");

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await JSRuntime.InvokeVoidAsync("removeContent", $"#{Id}");

        var options = new CalendarOptions
        {
            Id = Id,
            DefaultView = View,
            CalendarEvents = Events.Where(r => r.Status == CalendarEventStatus.Active).ToList()
        };

        await JSRuntime.InvokeVoidAsync("FullCalendarInterop.init", options, DotNetObjectReference.Create(this));

        await base.OnAfterRenderAsync(firstRender);
    }

    [JSInvokable]
    public async Task CalendarEventDeleted(string identifier)
    {
        var calendarEvent = Events.FirstOrDefault(x => x.Identifier == identifier);

        if (calendarEvent != null)
            calendarEvent.Status = CalendarEventStatus.Deleted;

        await EventsChanged.InvokeAsync(Events);
    }

    [JSInvokable]
    public Task SetDefaultView(string defaultView)
    {
        View = defaultView ?? CalendarView.ListWeek;

        return Task.CompletedTask;
    }

}

interop.js

    FullCalendarInterop = (() => {
        return {
            //main function to initiate the module
            init: function (options, dotNetReference) {
                var calendarEl = document.getElementById(options.id);
                var calendar = new window.FullCalendar.Calendar(calendarEl, {
                    plugins: ['interaction', 'dayGrid', 'timeGrid', 'list'],
    
                    isRTL: window.KTUtil.isRTL(),
                    header: {
                        left: 'prev,next today',
                        center: 'title',
                        right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
                    },
    
                    height: 800,
                    contentHeight: 750,
                    aspectRatio: 3,  // see: https://fullcalendar.io/docs/aspectRatio
    
                    views: {
                        dayGridMonth: { buttonText: 'month' },
                        timeGridWeek: { buttonText: 'week' },
                        timeGridDay: { buttonText: 'day' },
                        listWeek: { buttonText: 'list' }
                    },
    
                    defaultView: options.defaultView,
                    defaultDate: options.defaultDate,
    
                    editable: true,
                    eventLimit: true, // allow "more" link when too many events
                    navLinks: true,
                    events: options.calendarEvents,
    
                    eventRender: function (info) {
                        var event = $(info.event);
                        var element = $(info.el);
                        var view = $(info.view);
    
                        if (info.event.extendedProps && info.event.extendedProps.description) {
                            if (element.hasClass('fc-day-grid-event')) {
                                element.data('content', info.event.extendedProps.description);
                                element.data('placement', 'top');
                                window.KTApp.initPopover(element);
                            } else if (element.hasClass('fc-time-grid-event')) {
                                element.find('.fc-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
                            } else if (element.find('.fc-list-item-title').lenght !== 0) {
                                element.find('.fc-list-item-title').append('<div class="fc-description">' + info.event.extendedProps.description + '</div>');
                            }
                        }
    
                        element.find(".fc-bg").css("pointer-events", "none");
                        element.find(".fc-list-item-title.fc-widget-content").prepend(
                            "<span style='position: absolute; right: 5px;'>" +
                            "<button class='btn btn-icon btn-xs btn-circle btn-light' " +
                            "style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
                            "<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
                        element.find(".fc-content").append("<span style='position: absolute; top: 5px; right: 5px;'>" +
                            "<button class='btn btn-icon btn-xs btn-circle btn-light' " +
                            "style='height: 16px; width: 16px;' id='calendar_del_" + event.id + "'>" +
                            "<i class='icon-xs text-dark-50 flaticon2-cross'></i></button></span>");
                        element.find("#calendar_del_" + event.id).click(function () {
                            var eventId = event[0]._def.defId;
                            var eventIdentifier = event[0]._def.extendedProps.identifier;
    
                            //Remove popover
                            removeContent(".popover.fade.show.bs-popover-top");
                            //$(".popover.fade.show.bs-popover-top").remove();
    
                            dotNetReference.invokeMethodAsync('CalendarEventDeleted', eventIdentifier);
                            //$("#candidate_calendar").fullCalendar('removeEvents', eventId);
                        });
                    },
                    viewSkeletonRender: function(info) {
                        var view = $(info.view);
                        var defaultView = view[0].type !== null ? view[0].type : "";
    
                        dotNetReference.invokeMethodAsync('SetDefaultView', defaultView);
                        //alert(defaultView);
                    }
                });
    
                calendar.render();
            }
        };
    })();

function removeContent(target) {
    $(target).html("");
}

This is how we implement it in our pages.

Page.razor

@inject IJSRuntime JS

<EditForm Model="@_calendarEvents">
    <div class="card card-custom">
        <div class="card-header">
            <div class="card-title">
                <h3 class="card-label">
                    Calendar
                </h3>
            </div>
            <div class="card-toolbar">
                <a href="#" class="btn btn-light-primary font-weight-bold">
                    <i class="ki ki-plus "></i> Add Event
                </a>
            </div>
        </div>
        <div class="card-body">
            <InputCalendar Id="candidate_calendar" @bind-Events="_calendarEvents"></InputCalendar>
        </div>
    </div>
</EditForm>


@code {

    private List<CalendarEvent> _calendarEvents;

    protected override async Task OnInitializedAsync()
    {
        _calendarEvents =new List<CalendarEvent>
        {
            new CalendarEvent
            {
                Identifier = Guid.NewGuid().ToString(),
                Title = "Breakfast",
                Description = "Time to eat.",
                Start = DateTime.Now.AddHours(6),
                End = DateTime.Now.AddHours(7),
                SolidColor = CalendarEventSolidColor.Primary,
                DotColor = CalendarEventDotColor.Light,
                Status = CalendarEventStatus.Active
            },
            new CalendarEvent
            {
                Identifier = Guid.NewGuid().ToString(),
                Title = "Lunch",
                Description = "Time to eat.",
                Start = DateTime.Now.AddHours(10),
                End = DateTime.Now.AddHours(11),
                SolidColor = CalendarEventSolidColor.Warning,
                DotColor = CalendarEventDotColor.Light,
                Status = CalendarEventStatus.Active
            }
        };

        await base.OnInitializedAsync();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // If the calendar changes, we make an action here.

        await base.OnAfterRenderAsync(firstRender);
    }

}
Javier Contreras
  • 871
  • 7
  • 16