FullCalendar Events and Scheduling

#42 FullCalendar Events and Scheduling
8/14/2016

Summary

Learn how to incorporate FullCalendar into your Ruby on Rails application with listing and creating events. Using unobtrusive javascript, we can create a fast interactive calendar.
6
rails view calendar javascript ajax

Resources

FullCalendar - http://fullcalendar.io/
MomentJS - http://momentjs.com/
DateRangePicker - http://www.daterangepicker.com/
Source - https://github.com/driftingruby/042-fullcalendar

Downloads

FullCalendar 2.9.1 - https://github.com/arshaw/fullcalendar/releases/download/v2.9.1/fullcalendar-2.9.1.zip
MomentJS 2.14.1 - http://momentjs.com/downloads/moment.js

Install FullCalendar and MomentJS to your vendor directory.


Summary

If you want to use the gem instead, then you can ignore downloading FullCalendar and MomentJS. Instead, add the following to the Gemfile.

Gemfilegem 'fullcalendar-rails'
gem 'momentjs-rails'

Otherwise, be sure to include the required files in the vendor folder. The rest of the steps are the same regardless of which path you choose.

application.js//= require moment
//= require fullcalendar
application.css*= require fullcalendar
full_calendar.jsvar initialize_calendar;
initialize_calendar = function() {
  $('.calendar').each(function(){
    var calendar = $(this);
    calendar.fullCalendar({
      header: {
        left: 'prev,next today',
        center: 'title',
        right: 'month,agendaWeek,agendaDay'
      },
      selectable: true,
      selectHelper: true,
      editable: true,
      eventLimit: true,
      events: '/events.json',

      select: function(start, end) {
        $.getScript('/events/new', function() {});

        calendar.fullCalendar('unselect');
      },

      eventDrop: function(event, delta, revertFunc) {
        event_data = { 
          event: {
            id: event.id,
            start: event.start.format(),
            end: event.end.format()
          }
        };
        $.ajax({
            url: event.update_url,
            data: event_data,
            type: 'PATCH'
        });
      },
      
      eventClick: function(event, jsEvent, view) {
        $.getScript(event.edit_url, function() {});
      }
    });
  })
};
$(document).on('turbolinks:load', initialize_calendar);
view/visitors/index.html<div class='calendar'></div>
events_controller.rbclass EventsController < ApplicationController
  before_action :set_event, only: [:show, :edit, :update, :destroy]

  def index
    @events = Event.where(start: params[:start]..params[:end])
  end

  def show
  end

  def new
    @event = Event.new
  end

  def edit
  end

  def create
    @event = Event.new(event_params)
    @event.save
  end

  def update
    @event.update(event_params)
  end

  def destroy
    @event.destroy
  end

  private
    def set_event
      @event = Event.find(params[:id])
    end

    def event_params
      params.require(:event).permit(:title, :date_range, :start, :end, :color)
    end
end
events/_event.json.jbuilderdate_format = event.all_day_event? ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'

json.id event.id
json.title event.title
json.start event.start.strftime(date_format)
json.end event.end.strftime(date_format)

json.color event.color unless event.color.blank?
json.allDay event.all_day_event? ? true : false

json.update_url event_path(event, method: :patch)
json.edit_url edit_event_path(event)
events/new.js.erb$('#remote_container').html('<%= j render "new" %>');
$('#new_event').modal('show');
events/create.js.erb$('.calendar').fullCalendar(
  'renderEvent', 
  $.parseJSON("<%=j render(@event, format: :json).html_safe %>"),
  true
);
$('.modal').modal('hide');
events/edit.js.erb$('#remote_container').html('<%= j render "edit" %>');
$('#edit_event').modal('show');
events/update.js.erb$('.calendar').fullCalendar('removeEvents', [<%= @event.id %>]);
$('.calendar').fullCalendar(
  'renderEvent', 
  $.parseJSON("<%=j render(@event, format: :json).html_safe %>"), 
  true
);
$('.modal').modal('hide');
events/destroy.js.erb$('.calendar').fullCalendar('removeEvents', [<%= @event.id %>])
$('.modal').modal('hide');
events/index.json.jbuilderjson.array! @events do |event|
  date_format = event.all_day_event? ? '%Y-%m-%d' : '%Y-%m-%dT%H:%M:%S'
  json.id event.id
  json.title event.title
  json.start event.start.strftime(date_format)
  json.end event.end.strftime(date_format)
  json.color event.color unless event.color.blank?
  json.allDay event.all_day_event? ? true : false
  json.update_url event_path(event, method: :patch)
  json.edit_url edit_event_path(event)
end
635114?v=3&s=64
kobaltz said 5 months ago:

Based on some questions, the Event model looks like.

  create_table "events", force: :cascade do |t|
    t.string   "title"
    t.datetime "start"
    t.datetime "end"
    t.string   "color"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

1047150?v=3&s=64
wolfieorama said 2 months ago:

Thanks , good episode ... So how would you go about calculate the number of events per day and may be display that on the calendar ? 

635114?v=3&s=64
kobaltz said 2 months ago:

You have a few options on how to handle something like this. You could handle it on the server side with a model scope to check each individual day. However, that could be more taxing on the server if a calendar had several events; not to mention that you're calling the scope about 30 times per calendar view. An alternative would be to leverage client side calculations for the number of events. However, you could still run into issues with the client side taking a while to calculate the number of events.

Would your case use be for a smaller summary calendar? I know that the current version of FullCalendar does have a scaling feature where it will gracefully hide events and display a number of events for that day.


1047150?v=3&s=64
wolfieorama said 2 months ago:

thanks for a quick response: actually my use-case is quite simple and my events are quite controlled in the sense not more than 2 weeks form Today going forward and past events dont really matter.


So what I really want is to set a limit of events per day and when that limit is hit the background color of the day changes to indicate that the calendar for that day is full, hope that make sense ?

635114?v=3&s=64
kobaltz said 2 months ago:

Gotcha. It does make sense.

In that case, I would send an additional parameter with the JSON response of whether or not a day is blocked (which is calculated on the server side).

You can use the dayRender callback to change the color of a particular day based on the blocked days. Depending on how you are allowing new event creations, you would want to handle the validation there as well as the backend on the model.

This does get a bit tricky since you're having to validate on server side and render on client side as well.

Also check out this JSFiddle where they're adding a class to a date, dynamically, after the calendar is rendered.

1047150?v=3&s=64
wolfieorama said 2 months ago:

Yeah, thats sound like a good, good thing the events creation is being handled elsewhere not on the calendar, is calendar is purely for displaying.....

User will fist need to check on the calendar to see if there is a free day, so i am guessing the only server side check is if the events count limit has been hit or not ?

right ?

635114?v=3&s=64
kobaltz said 2 months ago:

Yeah, I'd calculate it on the server side and then pass the blocked days through the JSON render request. On the client side, put in the results of the client side validation. Overall, you just want to make sure that you're aren't allowing a client to go through a whole event record creation, only to tell them something that you already knew and could have told them from the beginning; that enrollment for that day is already filled.

1047150?v=3&s=64
wolfieorama said 2 months ago:

Sure thanks man !! will give it a shot 

1047150?v=3&s=64
wolfieorama said 2 months ago:

hay Man kinda got stuck, this is my calndar rendering code 


$(document).ready(function() {

  return $("#calendar").fullCalendar({

    header: {

      left: 'title',

      center: 'agendaDay,agendaWeek,month',

      right: 'today prev,next',

    },

    weekends: false,

    events: '/leave_events.json',

    dayRender: function( date, cell ) {


      if (moment().diff(date,'days') > 0){

        cell.css("background-color", "silver");

      }

    }

  });

});


and here is my json object builder


json.array!(@leave_events) do |leave_event|

  json.extract! leave_event, :id, :user_id, :team_id

  json.start leave_event.start_time

  json.end leave_event.end_time

end


still cant extract the count of events per day ... any tips 

635114?v=3&s=64
kobaltz said 2 months ago:

You could do something like this

class LeaveEvent < ActiveRecord::Base
  scope :events_on_date, -> (date)  { where('start_time <= ? AND end_time >= ?', date, date).size }
 ...
end

Then use the scope within the JSON builder.

1047150?v=3&s=64
wolfieorama said 2 months ago:

the thing thats troubling me now is how to pass the date in to the scope in the jbuilder without hardcoding it 


```json.array!(@leave_events) do |leave_event|

  json.extract! leave_event, :id, :user_id, :team_id

  json.start leave_event.start_time

  json.end leave_event.end_time

  json.events_on_date LeaveEvent.events_on_date(date) // this line is where I can figure out stuff 

  json.max_allowable_leave leave_event.team.max_allowable_leave

end```


see the rest of `calendar.js`


```$(document).ready(function() {

  return $("#calendar").fullCalendar({

    header: {

      left: 'title',

      center: 'agendaDay,agendaWeek,month',

      right: 'today prev,next',

    },

    weekends: false,

    events: '/leave_events.json',

    eventLimit: 1,

    dayRender: function( date, cell ) {

      var events_on_date = ;

      $.getJSON('leave_events.json', function(json) {

        for (var i = 0; i < json.length; i++) {

          console.log(events_on_date);

          if (events_on_date > json[0].max_allowable_leave){

            cell.css("background-color", "red");

          }

        }

      });

      if (moment().diff(date,'days') > 0){

        cell.css("background-color", "silver");

      }

    }

  });

});```

00000000000000000000000000000000?d=mm&f=y&s=64
[email protected] said 2 months ago:

First of all Great episode and thank you for these wonderful videos!

Question:  How do i populate calendar with recurring events if: 

I'm using ice_cube gem to build reccuring events in my rails app. i can get my recurring events to be created and show with this: 

            - @event.converted_schedule.occurrences_between(@ event.start_date, @ event.end_date).each do |date|

              = date.strftime("%A %B %d, %Y")

where i can get all the recurrences. 

I dont know how to do it! Can you please offer some suggestions? I'm a beginner in programming!

thanks again

olsi

Login to Comment