Features

  • ASP.NET Core 2.0 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 AJAX calls
  • Lightweight backend that build using ASP.NET Core Web API
  • Entity Framework Core 2.0
  • SQL Server (LocalDB)
  • Visual Studio 2017
  • 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. Buy a license.

Initializing the Weekly Calendar

javascript-weekly-calendar-asp.net-core-initialization.png

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

javascript-weekly-calendar-asp.net-core-data-access.png

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 properies:

  • 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:

javascript-weekly-calendar-asp.net-core-sql-server-database.png

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...:

javascript-weekly-calendar-asp.net-core-web-api-controller.png

In the next dialog, choose "API Controller with actions, using Entity Framework":

javascript-weekly-calendar-asp.net-core-api-entity-framework.png

Select the model class (Event), data context class (CalendarContext) and confirm the controller name (EventsController):

javascript-weekly-calendar-asp.net-core-api-controller-details.png

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

javascript-weekly-calendar-asp.net-core-loading-events.png

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;
}