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.
15
rails view calendar javascript ajax 14:19 min

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 about 1 year 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 11 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 11 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 11 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 11 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 11 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 11 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 11 months ago:

Sure thanks man !! will give it a shot 

1047150?v=3&s=64
wolfieorama said 11 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 11 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 11 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 11 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

24507266?v=3&s=64
nekifirus said 9 months ago:

Hello!

Code in

events/_event.json.jbuilder 

not DRY. How I can refactor it?

18031943?v=3&s=64
jinw96 said 9 months ago:

Thanks for your nice lecture!

I have some trouble with my _form file.

It's datetime doesn't relate to the time that I drag.

How can I relate dragged time to form's time?

Can you show me your form file??

18031943?v=3&s=64
jinw96 said 9 months ago:

Oh, I saw your _form file in your video..

But what is the Date.today? 

Is it a model you made??

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

https://ruby-doc.org/stdlib-2.1.1/libdoc/date/rdoc/Date.html#method-c-today

Date.today is a ruby core class and method which creates a date object of the current date.

Photo
Adário Muatelembe said 8 months ago:

I'm following all the steps of the episode but the calendar is not being viewed. what should I do?

Photo
Adário Muatelembe said 8 months ago:

Greetings.

I have a difficulty, I have implemented the canderary and it works perfectly.
But it does not interpret the jQuery validations made in the form.

And how to recall alerts on creation, update and delete


Excuse me, my English is not a big deal, but I tried. Lol

00000000000000000000000000000000?d=mm&f=y&s=64
Erick said 8 months ago:

Greetings:

I enjoyed your video_cast episode on full_calendar and have tried to implement, but seem to be missing some key parts:  1) you mention visitor/view, but give no background on who/what/why that view is needed, nor what its db would contain, 2) using Ruby 2.4.0-rc1 and rails 5.0.1, my application tries to open html code instead of json -- how dit you create your scaffold/modify routes/or ?? to stop that behavior for the full_calendar actions?, and finally, 3) do you have a copy of this rails app demo for download?

Would like to know if this is a rails 5/ruby issue or my bad typing. 

Thanks for your consideration.

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

The visitor view is just a simple controller and has an action. Within there is a DIV which contains the ID calendar which is where I'm displaying the calendar. You would place the calendar div where ever appropriate for your application.

For the requests responding as HTML instead of JSON, this could be how you're requesting the data. I'm using format: :json which will make a call to get the json data by default. I didn't do anything special in creating the events controller/model. Check your application logs to see how it is being handled. You might get some hints there. 

For the JSON list of events in the jQuery, I'm calling the url /events.json which would be the same in Ruby as calling events_path(format: :json).

Hope this helps.

00000000000000000000000000000000?d=mm&f=y&s=64
Erick said 8 months ago:

Thank you for your response.

Yes I am (mostly) getting json actions. I have a problem since you call templates _new and _edit, and application.json.erb, you give no indicator of what should / may be in those files.

Would you be willing to share the code for the missing modules? I also get an error undefined method `midnight' for nil:Nil in all_day_event?

Again, thank you for your consideration. Your presentation has come the closest to working of the several other "add full calendar" to rails tutorials that I have tried to implement. I have found that I had to change the suffix 'js' to 'son' on the new.json.erb and _new.json.erb files for rails5 not to complain about missing templates. In your video cast, I can see that you call out 'js' as you show on the page of content under the video. ????

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

You can access the source code for this episode at https://github.com/driftingruby/042-fullcalendar

00000000000000000000000000000000?d=mm&f=y&s=64
Erick said 8 months ago:

Awesome!

Looks great.  I need to read up on Faker used in the seed.  I have never seen that but I easily see its value.

Thank you so much for making the code available!  I will be able to study and lean from it for quite a while.

Thanks again.

00000000000000000000000000000000?d=mm&f=y&s=64
Erick said 8 months ago:

I intend to incorporate your code into an app I am creating for a friend who wants to be an author.  As your seed program shows, it can be easy to overload the viewer.  To simplify, I plan to add to the event record a couple of additional fields, and provide a mechanism preferably on the calendar itself where the additional fields would have collection_select menus for the new event record entries (multiple selections allowed).  He could then have calendar views for specific objects (characters, story plot, etc.).  In addition, one of the fields would hold an 'offset' value which would override the Date.today and move it forward/backward in time.   Thus if he were writing a period piece about the 15th century, I would expect the calendar for that selected calendar year would show up in the calendar view; i.e., "Feb 2017" would become "Feb 1517" if the offset were -500 years.

Doing a code search for date variations does not show me where that banner date value is being generated.  

Any hints or comments that you or your viewers might be willing to share?

Thanks 

00000000000000000000000000000000?d=mm&f=y&s=64
frank004 said 8 months ago:

Love to see this one with ice_cube recurrence.

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

Sounds like a great idea. Would definitely have to follow an Outlook Calendar style functionality where a single occurrence could be edited of a series. Worth looking into.

00000000000000000000000000000000?d=mm&f=y&s=64
kalistalanti said 7 months ago:

Heyy! I've been on this for days, need help :( 

So I'm trying to combine your code with AdminLTE's for the drag and drop event on the calendar (https://almsaeedstudio.com/themes/AdminLTE/pages/calendar.html).

Sadly I'm currently stuck on how to manually save or input the event_params without the form, because they have this js code for the drop

 drop: function (date, allDay) { 
        var originalEventObject = $(this).data('eventObject');
      
        var copiedEventObject = $.extend({}, originalEventObject);
        copiedEventObject.start = date;
        copiedEventObject.backgroundColor = $(this).css("background-color");
        copiedEventObject.borderColor = $(this).css("border-color")
        $('.calendar').fullCalendar('renderEvent', copiedEventObject, true);
    }

So, I'm wondering how to input the event to /events.json, thank you very much for the time and  help, it means so much! 



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

I think I'll do a followup episode on the FullCalendar with additional features, including the drag/drop support and recurring events.

00000000000000000000000000000000?d=mm&f=y&s=64
kalistalanti said 7 months ago:

Aah thank you for the fast response! Ok I'll wait for it, thanks for everything you've done, you're a savior!:)

8132386?v=3&s=64
Rehan Khan said 6 months ago:

@kobaltz i would like to know is there built-in time validation, like if user select 2-3-2017 and 12:00pm to 1:00pm then if user again try to choose same date with same time range.

can you tell me me is there any option already available or not?

00000000000000000000000000000000?d=mm&f=y&s=64
johnraymondsamonte98 said 6 months ago:

Hi can i have some question about the code? How can i delete the month and week. because i need only the day it is possible? Thank you :)

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

You should be able to initialize the calendar with these kind of options to do this. In the header, you would only pass agendaDay and then set the default view to basicDay.


   header: {
    left: 'prev,next today',
    center: 'title',
    right: 'agendaDay'
   },
  defaultView: 'basicDay',

00000000000000000000000000000000?d=mm&f=y&s=64
johnraymondsamonte98 said 6 months ago:

Thank you for answering my question, I have 2nd question. What if i click the specific day in the calendar then after clicking it we want to show the agendaDay it is possible? Thankyou :)


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

Yes it is possible, and it is actually the behavior on the demo calendar on https://fullcalendar.io/

00000000000000000000000000000000?d=mm&f=y&s=64
johnraymondsamonte98 said 6 months ago:

How can i change the time ? for example. our time starts in 10am and it will end in 5pm how do i change the time? and i want to change the interval of the time. from 30 mins to 15 mins .

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

I'll plan on covering more on FullCalendar and additional functionalities.

8132386?v=3&s=64
Rehan Khan said 6 months ago:

Hi  i would like to know is there built-in time validation, like if user select 2-3-2017 and 12:00pm to 1:00pm then if user again try to choose same date with same time range.

can you tell me me is there any option already available or not?

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

You can do something like this with model validations to not allow overlapping times. On the callback within the FullCalendar, you would handle the necessary alerts there.

00000000000000000000000000000000?d=mm&f=y&s=64
apersad718 said 5 months ago:

Hi! Almost a year later and this episode is still helping people. Thanks so much! I modified the code a bit (mainly CSS so shouldn't cause too much of a problem). My overall plan is to add users and play around with that, but I'm getting ahead of myself.

I tried to upload it to Heroku but I get a 500 error when I load the page and try to click on a day to add an event. Logs tell me it's getting a 500 on `/events/new?_=1494878802742`. Could you potentially think of a reason why?

I did switch from sqlite3 to postgreSQL. Could that be causing the problem? I switched because Heroku plays nicer with pg than sqlite.

Edit: I feel like I should mentioned that it works perfectly when I test locally with the rails server

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

Is your local env also using Postgres? 

Do you have any other logs?

00000000000000000000000000000000?d=mm&f=y&s=64
apersad718 said 5 months ago:

Hi again! Thanks for replying. I figured it out. Turns out I'm just a silly programmer and forgot to run my rake tasks. Works like a charm now.

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

Great to hear!

Photo
Vaibhav Singhal said 4 months ago:

Sir The video was really very helpful in my project.But i am really stuck in using recurring events using ice cube.Please soon do a video lesson for the same

Photo
Jack Chang said 4 months ago:

I  just downloaded  and installed your example from https://github.com/driftingruby/042-fullcalendar and seems it has the same issue I have encountered.

To reproduce the issue:

1.  create an new event or update (either edit or drag&drop) an existing event. (everything is good so far)

2 . click on next month button, then click to go back to current month

3. you will see two duplicate events you just created/updated

Any clue?

Photo
Jack Chang said 4 months ago:

NVM, I found the problem in  https://github.com/driftingruby/042-fullcalendar.

In app/views/events/create.js.erb and  app/views/events/update.js.erb

$('.calendar').fullCalendar(

 'renderEvent',

  $.parseJSON("<%=j render(@event, format: :json).html_safe %>"),

  true   //  <-----

);

the 'stick' atttibute for  'renderEvent'  method  should be set to false




Photo
Alejandra River said 3 months ago:

Can you help me please implement it in java

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

Sorry, I primarily focus on Ruby. However, check out their docs at https://fullcalendar.io/docs/ to see some of their examples that they have.

Photo
saad Benmakhlouf said 3 months ago:

 i need your help

i donwload : https://github.com/driftingruby/042-fullcalendar

how can i run it ??

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

What environment are you working in?

Photo
saad Benmakhlouf said 3 months ago:

i have a macbook.

i just download it from github a few seconds ago

i didn't do any change

i have npm install 

i want to run it but i couldn't 

Thanks for your quick answer and your help 

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

It is a Rails application, so you would need to have Ruby installed. Check out https://www.driftingruby.com/episodes/getting-started-ruby-on-rails-development-environment on getting a non-system ruby installed on your environment. 

Then in the app root, you should be able to just do 

gem install bundler #installs bundle cli
bundle # installs gem dependencies
rake db:migrate # creates the SQLite3 Schema
rails s # starts the development server

Photo
saad Benmakhlouf said 3 months ago:

can i give you my teamviewer (id and password) ??

Photo
Usama Khan said 10 days ago:

i cant get it working following the steps but calendar does not show

Login to Comment