How to Integrate and Synchronize Google Calendar with Your Blazor Application
A calendar is a system that helps organize days and has been around for ages. In the age of the internet, it serves as an important productivity tool for individuals and organizations to remember and schedule meetings, leaves, and notable events.
The GrapeCity team introduced the C1Calendar control for the Blazor edition in its 2021 v1 release. The C1 Calendar is a highly customizable control that allows users to create interactive web applications and offers various advantages apart from scheduling and allows users to customize as per requirements.
This blog will review how the C1 Calendar can be used to synchronize the appointments from the popular Google Calendar containing multiple calendars. We will also discuss how to add a new appointment using the C1 Calendar to Google Calendar, as well as:
- Working with Google Calendar API
- Integrate C1Calendar to Blazor app
- Show Appointments to C1Calendar from Google Calendar
- Add appointments to Google Calendar
This blog will cover the basics of Google Calendar and how users can leverage the Google Calendar Library and C1 Calendar by creating a sample application.
Working with Google Calendar API
Google Calendar is associated with the user's Google account. The calendar has one primary calendar, and it can contain several independent calendars, which may be shared among the users.
In this blog, we will synchronize one of the calendars (the primary calendar) with the C1 Calendar control.
Understanding Google Calendar and Events
A calendar can be represented as a collection of similar events with additional information such as location, summary, time zone, etc. Each calendar is associated with a unique Calendar ID, whereas an event would be related to a specific date, and each event would have a unique Event ID.
These events can be set for a specific period or the complete day. In this case, each instance of Appointment would represent a single event. Google Calendar allows users only to parse events list from one calendar at a time using the Google Calendar API. These events from a given calendar can be fetched using Events.list() function by passing the calendar ID as the parameter.
Find more information on the supported functions here.
For our demo application, we would follow the below steps:
Creating Credentials.json File
Google API uses the OAuth 2.0 authentication and authorization and generates the credentials based on the specified scopes for API. The resources and permissions can be granted using the scopes. Accessing the Google Calendar API from the code, the access token is required, which can be retrieved from the credentials.
User consent can be set by logging in to the Google account, and the credential file can be generated. We need to be careful while setting the scopes since the access token will allow us to perform only those operations for which permission is granted using the scope.
Find more information about how the credentials.json file can be generated here.
Using Google Calendar API
For using the Google Calendar API in the project, we are required to install Google.Apis.Calendar.v3 NuGet package from NuGet package manager.
Accessing Appointments from Google Calendar using API
We would need the Google Calendar NuGet package and the credentials.json file to get started. The credentials.json file must be included in the project. Now, we would write a service to access the Data from the Google Calendar using the generated credentials.
public class GoogleService
{
string[] Scopes = { CalendarService.Scope.Calendar };
string ApplicationName = "Blazor Calendar Scheduler";
CalendarService Service;
public GoogleService()
{
UserCredential credential;
using (FileStream stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, Scopes, "admin", CancellationToken.None, new FileDataStore("token.json", true)).Result;
}
Service = new CalendarService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = ApplicationName });
}
….
}
Now, our GoogleService class is ready to access the data from the Google Calendar using Apis. For converting the events information into C# List, we require the adapter class to map the events data to a list after fetching the events list.
Since we will display and add the appointments, we would write the methods for Fetching and Adding events from Calendar.
public class GoogleService
{
...
/// <summary>
/// Method to fetch Event List
/// </summary>
/// <param name="date"></param>
/// <param name="calenderId"></param>
/// <returns></returns>
public List<Appointment> GetEvents(DateTime date, string calenderId = "primary")
{
try
{
EventsResource.ListRequest request = Service.Events.List(calenderId);
request.TimeMin = date;
request.MaxResults = 2500;
Events events = request.Execute();
List<Appointment> eventDatas = new List<Appointment>();
if (events.Items != null && events.Items.Count > 0)
{
foreach (Event eventItem in events.Items)
{
if (eventItem.Start == null && eventItem.Status == "cancelled")
{
continue;
}
DateTime start;
DateTime end;
if (string.IsNullOrEmpty(eventItem.Start.Date))
{
start = (DateTime)eventItem.Start.DateTime;
end = (DateTime)eventItem.End.DateTime;
}
else
{
start = Convert.ToDateTime(eventItem.Start.Date);
end = Convert.ToDateTime(eventItem.End.Date);
}
Appointment eventData = new Appointment()
{
Id = eventItem.Id,
Subject = eventItem.Summary,
StartTime = start,
EndTime = end,
Location = eventItem.Location,
Description = eventItem.Description,
IsAllDay = !string.IsNullOrEmpty(eventItem.Start.Date)
};
eventDatas.Add(eventData);
}
}
return eventDatas;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new List<Appointment>();
}
}
/// <summary>
/// Method to Create New Events
/// </summary>
/// <param name="apt"></param>
/// <param name="calenderId"></param>
public void CreateEvent(Appointment apt, string calenderId = "primary")
{
Event newEvent = new Event()
{
Summary = apt.Subject,
Location = apt.Location,
Description = apt.Description,
Start = new EventDateTime()
{
DateTime = apt.StartTime,
Time Zone = "Asia/Kolkata",
},
End = new EventDateTime()
{
DateTime = apt.EndTime,
TimeZone = "America/Los_Angeles",
}
};
EventsResource.InsertRequest request = Service.Events.Insert(newEvent, calenderId);
Event createdEvent = request.Execute();
}
}
Our service is now ready to interact with the Google Calendar for fetching and creating appointments.
Integrating C1 Calendar to Blazor App
The C1 Calendar can be integrated into the Blazor app by installing the required NuGet package C1.Blazor.DateTimeEditors. The C1.Blazor.DateTimeEditors NuGet package will install its dependency NuGet packages C1.Blazor.Core, C1.Blazor.Calendar, C1.Blazor.Input and some other packages.
After installing the required packages, the C1Calendar can be added using the following code:
@page "/"
@using C1.Blazor.Calendar
@using C1.Blazor.Core
<C1Calendar></C1Calendar>
We are required to add the following scripts and CSS for rendering the C1 controls properly:
<link rel="stylesheet" href="~/_content/C1.Blazor.Core/styles.css" />
<link rel="stylesheet" href="~/_content/C1.Blazor.Calendar/styles.css" />
<link rel="stylesheet" href="~/_content/C1.Blazor.Input/styles.css" />
<link rel="stylesheet" href="/_content/C1.Blazor.DateTimeEditors/styles.css" />
<script src="~/_content/C1.Blazor.Core/scripts.js"></script>
<script src="~/_content/C1.Blazor.Calendar/scripts.js"></script>
<script src="~/_content/C1.Blazor.Input/scripts.js"></script>
Display Appointments in C1 Calendar from Google Calendar
As we have added the C1 Calendar to the app, an event list is also fetched from the Google Calendar. Now, we would display the events data inside the C1 Calendar by customizing the Day Slot using DaySlotTemplate. Using DaySlotTemplate, the UI can be customized to display the events list. We will display all the events for the day as a strip for the specified day using the template, and the pop-up will be displayed on clicking on the event to get the information about the targeted event.
@page "/"
@inject GoogleService GoogleService
@inject IJSRuntime jsRuntime
@using GC_Blazor_Server.Data
@using C1.Blazor.DateTimeEditors
@using C1.Blazor.Calendar
@using C1.Blazor.Input
@using C1.Blazor.Core
<div class="container-fluid" style="width:100%;">
…
<C1Calendar @ref="CustomCalender" Style="@_calendarStyle" HeaderStyle="@_calendarHeaderStyle" DayOfWeekStyle="@_calendarDayOfWeekStyle" DayStyle="@CalendarDayStyle" TodayStyle="@_calendarToDayStyle" SelectionStyle="@_calendarSelectionStyle" DisplayDateChanged="displayDateChanged" OnSelectionChanged="onSelectionChanged">
<DaySlotTemplate>
<div style="width:100%;height:100%;min-height:100px;max-height:140px">
<div style="text-align:right;height:20px;"><button @onclick="() => ShowCreateEventPopup((DateTime)context.Date)" class="oi oi-plus btn-apt"></button></div>
<div class="date">@context.Date.ToString("dd")</div>
@foreach (var HolidayData in Holiday_DataSource.FindAll(e => e.StartTime.Date == (DateTime)context.Date).Select((value, index) => new { index, value}))
{
if(HolidayData.index > 2)
{
break;
}
var eventId = HolidayData.value.Id;
var Type = "Holiday";
var idx = HolidayData.index + 1;
@if (HolidayData.value.Subject == "" || HolidayData.value.Subject == null)
{
<div class="event-data holiday" @onclick="() => DisplayPopUp(eventId, Type)">Holiday @idx</div>
}
else
{
<div class="event-data holiday" @onclick="() => DisplayPopUp(eventId, Type)">@HolidayData.value.Subject</div>
}
}
@foreach (var EventData in Meeting_DataSource.FindAll(e => e.StartTime.Date == (DateTime)context.Date).Select((value , index) => new {index, value }))
{
if(EventData.index > 2)
{
break;
}
var eventId = EventData.value.Id;
var Type = "Event";
var idx = EventData.index + 1;
@if (EventData.value.Subject == "" || EventData.value.Subject == null)
{
<div class="event-data event" @onclick="() => DisplayPopUp(eventId, Type)">Event @idx</div>
}
else
{
<div class="event-data event" @onclick="() => DisplayPopUp(eventId, Type)">@EventData.value.Subject</div>
}
}
</div>
</DaySlotTemplate>
</C1Calendar>
</div>
</div>
@code
{
C1Calendar CustomCalender;
DateTime? EventStartDate = DateTime.Today, EventEndDate = DateTime.Today;
private DateTime SelectedDate;
private List<Appointment> Meeting_DataSource;
private List<Appointment> Holiday_DataSource;
private int MonthBufferPeriod = 2;
readonly C1Style _calendarStyle = new C1Style()
{
Width = "100%",
Height = "100%",
BorderWidth = 1,
BorderStyle = C1StyleBorderStyle.Solid,
BorderColor = C1Color.Black
};
readonly C1Style _calendarHeaderStyle = new C1Style() { };
readonly C1Style _calendarDayOfWeekStyle = new C1Style() { };
static readonly C1Style CalendarDayStyle = new C1Style()
{
FontSize = 20,
};
readonly C1Style _calendarToDayStyle = new C1Style(CalendarDayStyle)
{
BackgroundColor = "#efefef",
Color = "#000000",
FontWeight = "bold"
};
readonly C1Style _calendarSelectionStyle = new C1Style(CalendarDayStyle)
{
Color = "#000000",
BackgroundColor = "#dedede",
FontWeight = "bold"
};
protected async override Task OnInitializedAsync()
{
await base.OnInitializedAsync();
SelectedDate = DateTime.Today;
GetEventList(SelectedDate);
}
private void GetEventList(DateTime SelectedDate)
{
SelectedDate = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
SelectedDate = SelectedDate.AddMonths(-MonthBufferPeriod);
Meeting_DataSource = GoogleService.GetEvents(SelectedDate, "primary");
Holiday_DataSource = GoogleService.GetEvents(SelectedDate, "en.indian#holiday@group.v.calendar.google.com");
}
public void displayDateChanged(object sender, PropertyChangedEventArgs<DateTime> e)
{
if ((e.OldValue.Month != e.NewValue.Month) || (e.OldValue.Year != e.OldValue.Year))
{
GetEventList(e.NewValue);
StateHasChanged();
}
}
private async void CreateEvent()
{
Appointment NewEvent = new Appointment
{
StartTime = SelectedDate,
EndTime = SelectedDate,
Subject = EventSubject,
Location = EventLocation,
Description = EventDescription
};
GoogleService.CreateEvent(NewEvent);
GetEventList(SelectedDate);
CustomCalender.Refresh();
StateHasChanged();
}
public void onSelectionChanged(object sender, CalendarSelectionChangedEventArgs e)
{
SelectedDate = (DateTime)CustomCalender.SelectedDate;
}
}
Adding Appointments to Google Calendar
In the previous step, we have added + sign to the template to create a new event. When clicking this icon, a pop-up is displayed where users can add the information. On submitting the information, a new event would be created in Google Calendar using the event.Insert() method.
public void CreateEvent(Appointment apt, string calenderId = "primary")
{
Event newEvent = new Event()
{
Summary = apt.Subject,
Location = apt.Location,
Description = apt.Description,
Start = new EventDateTime()
{
DateTime = apt.StartTime,
TimeZone = "Asia/Kolkata",
},
End = new EventDateTime()
{
DateTime = apt.EndTime,
TimeZone = "America/Los_Angeles",
}
};
EventsResource.InsertRequest request = Service.Events.Insert(newEvent, calenderId);
Event createdEvent = request.Execute();
}
Now our application is ready with the C1 Calendar to show and add events to Google Calendar using the Google Calendar API. Here is the screenshot of how our C1 Calendar is looking after customizing to show the Google Calendar events information.
Download the sample here.
Happy Coding!