Features
ASP.NET Core MVC application
Uses JavaScript weekly calendar component from DayPilot Pro for JavaScript
Date picker for switching the visible week
Loading calendar data from the server using HTTP calls
Lightweight backend that build using ASP.NET Core Web API
Entity Framework Core
SQL Server (LocalDB)
Visual Studio
Includes a trial version of DayPilot Pro for JavaScript (see License below)
License
Licensed for testing and evaluation purposes. Please see the license agreement included in the sample project. You can use the source code of the tutorial if you are a licensed user of DayPilot Pro for JavaScript.
Initializing the Weekly Calendar
Our ASP.NET Core web application has a single view (Views/Home/Index.cshtml
) that displays a weekly calendar using DayPilot JavaScript calendar component.
The weekly calendar requires the DayPilot Pro library to be included:
<script src="~/js/daypilot/daypilot-all.min.js"></script>
We need to add a placeholder div at the target location:
<div id="dp"></div>
And the last step is the initialization code that renders the calendar at the location specified using the placeholder:
<script>
var dp = new DayPilot.Calendar("dp");
dp.viewType = "Week";
dp.init();
</script>
This simple config displays an empty weekly calendar.
Data Access using Entity Framework
We want the weekly calendar to display event data from an SQL Server database. First, we need to create our data model and data access layer using Entity Framework.
Our model class will be called Event and it will define the basic calendar event properties:
id
start
end
text
color
The id
, start
, end
and text
properties are required by the calendar control. The color
property is a custom field that we will use to store an event color.
Our Event class will look like this:
public class Event
{
public int Id { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Text { get; set; }
public string Color { get; set; }
}
We also need to define a database context class that will manage the Event class database access for us:
public class CalendarContext : DbContext
{
public DbSet<Event> Events { get; set; }
public CalendarContext(DbContextOptions<CalendarContext> options):base(options) { }
}
Both classes are defined in Models/Data.cs
file:
using Microsoft.EntityFrameworkCore;
using System;
namespace TutorialWeeklyCalendarAspNetCore.Models
{
public class CalendarContext : DbContext
{
public DbSet<Event> Events { get; set; }
public CalendarContext(DbContextOptions<CalendarContext> options):base(options) { }
}
public class Event
{
public int Id { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
public string Text { get; set; }
public string Color { get; set; }
}
}
We want to use SQL Server database called DayPilot.TutorialWeeklyCalendar
to store the calendar data. This is our database connection string:
Server=(localdb)\\mssqllocaldb;Database=DayPilot.TutorialWeeklyCalendar;Trusted_Connection=True
We will store the connection string under "CalendarContext"
key in the "ConnectionStrings"
section of appsettings.json
:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"CalendarContext": "Server=(localdb)\\mssqllocaldb;Database=DayPilot.TutorialWeeklyCalendar;Trusted_Connection=True"
}
}
The last step is to register the CalendarContext
service in Startup.cs
:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TutorialWeeklyCalendarAspNetCore.Models;
using Microsoft.EntityFrameworkCore;
namespace TutorialWeeklyCalendarAspNetCore
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<CalendarContext>(options => options.UseSqlServer(Configuration.GetConnectionString("CalendarContext")));
}
// ...
}
}
Now the data model and database configuration are ready and we need to create the initial migration classes. The migration classes will be generated in Migrations directory and will contain code that can generate the database from our model.
Run the following command in the Package Manager Console:
Add-Migration InitialCreate
Now the migrations are ready and we can use the database initialization command to create the database:
Update-Database
You can use the Visual Studio SQL Server Object Explorer to check that the database has been created:
API Controller for Handling the Calendar Data
Now we need to create a Web API controller that will let us manage the calendar event data using AJAX calls. Visual Studio can help us with generating a new Web API controller from our data model class. Right click Controllers in the Solution Explorer and choose Add -> Controller...:
In the next dialog, choose "API Controller with actions, using Entity Framework":
Select the model class (Event
), data context class (CalendarContext
) and confirm the controller name (EventsController
):
Now we have a new controller class (Controllers/EventsController.cs
) with REST endpoints for common operations:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TutorialWeeklyCalendarAspNetCore.Models;
namespace TutorialWeeklyCalendarAspNetCore.Controllers
{
[Produces("application/json")]
[Route("api/Events")]
public class EventsController : Controller
{
private readonly CalendarContext _context;
public EventsController(CalendarContext context)
{
_context = context;
}
// GET: api/Events
[HttpGet]
public IEnumerable<Event> GetEvents([FromQuery] DateTime start, [FromQuery] DateTime end)
{
return _context.Events;
}
// GET: api/Events/5
[HttpGet("{id}")]
public async Task<IActionResult> GetEvent([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var @event = await _context.Events.SingleOrDefaultAsync(m => m.Id == id);
if (@event == null)
{
return NotFound();
}
return Ok(@event);
}
// PUT: api/Events/5
[HttpPut("{id}")]
public async Task<IActionResult> PutEvent([FromRoute] int id, [FromBody] Event @event)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != @event.Id)
{
return BadRequest();
}
_context.Entry(@event).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!EventExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Events
[HttpPost]
public async Task<IActionResult> PostEvent([FromBody] Event @event)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Events.Add(@event);
await _context.SaveChangesAsync();
return CreatedAtAction("GetEvent", new { id = @event.Id }, @event);
}
// DELETE: api/Events/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteEvent([FromRoute] int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var @event = await _context.Events.SingleOrDefaultAsync(m => m.Id == id);
if (@event == null)
{
return NotFound();
}
_context.Events.Remove(@event);
await _context.SaveChangesAsync();
return Ok(@event);
}
private bool EventExists(int id)
{
return _context.Events.Any(e => e.Id == id);
}
}
}
Loading Calendar Data
Now we have our backend ready and we can configure the weekly calendar to load the event data from the server. This can be done using dp.events.load()
method:
<script>
var dp = new DayPilot.Calendar("dp");
dp.viewType = "Week";
dp.init();
dp.events.load("/api/events");
</script>
This method will use the specified URL (/api/events
) to load the event data and display them. It adds the visible range (start and end of the current week) to the URL as start
and end
query string parameters:
/api/events?start=2017-10-15T00:00:00&end=2017-10-22T00:00:00
We will modify the GetEvents()
method of the API controller (EventsController
class) to read the query string parameters and use them to limit the event data:
// GET: api/Events
[HttpGet]
public IEnumerable<Event> GetEvents([FromQuery] DateTime start, [FromQuery] DateTime end)
{
return from e in _context.Events where !((e.End <= start) || (e.Start >= end)) select e;
}
History
December 20, 2020: Upgraded to DayPilot Pro for JavaScript 2020.4.4807; layout CSS updates
October 18, 2017: Initial release