We will create a Rails 6 calendar Application using StimulusJs and Tui Calendar. To begin, we first create a new Rails application.
rails new calendar --database=postgresql
Once we have created our application, we then proceed to install StimulusJs.
rails webpacker:install:stimulus
We then proceed to install Tui Calendar using yarn.
yarn add tui-calendar
Modeling Data
Once we have all our dependencies installed we can proceed to scaffolding the data we wish to store in our application. We would create a model called Event with the following attributes and types:
CalendarID : Integer
Title : string
Location: string
start: datetime
end: datetime
We run our scaffold generator to create the model, views and the controller for our data.
rails g scaffold schedule title:string calendar_id:integer start:datetime end:datetime location:string
rails db:migrate
We then define the root path of our application
config/routes.rb
root 'schedules#index'
Displaying the Calendar
Once the initial setup is done, we can now proceed to display our calendar. Navigate to
app/views/schedules/index.html.erb and clear the scaffolded index page. We create a div tag and give it an id of calendar. we also provide some json data to the div tag. We wrap this div tag in a stimulus controller called calendar.
app/views/schedules/index.html.erb
<div data-controller="calendar">
<%= tag.div nil, data: {schedules: @schedules .to_json}, id: "calendar"%>
</div>
We then proceed to create our stimulus controller called ‘calendar_controller.js’ under the path app/javascript/controllers/calendar_controller.js and export the class
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
}
}
Once we have done that we import several libraries:
import Rails from "@rails/ujs"
import 'tui-time-picker/dist/tui-time-picker.css';
import "tui-calendar/dist/tui-calendar.css";
import Calendar from "tui-calendar";
Note: We simply follow the steps on the github document page to import Calendar and its stylesheets. Once we have done that, we create a new instance of Calendar, and define its attributes such as id, name, defaultView, color. This provided in the code below:
calendar = new Calendar(document.getElementById('calendar'), {
id: "1",
name: "My Calendar",
defaultView: 'month',
color: '#00a9ff',
bgColor: '#00a9ff',
dragBgColor: '#00a9ff',
borderColor: 'red',
milestone: true, // Can be also ['milestone', 'task']
scheduleView: true, // Can be also ['allday', 'time']
useCreationPopup: true,
useDetailPopup: true,
template: {
popupDetailRepeat: function(schedule) {
return 'Repeat : ' + schedule.recurrenceRule;
},
popupStateFree: function() {
return 'Free';
},
milestone: function(schedule) {
return '<span style="color:red;"><i class="fa fa-flag"></i> ' + schedule.title + '</span>';
},
milestoneTitle: function() {
return 'Milestone';
},
task: function(schedule) {
return ' #' + schedule.title;
},
taskTitle: function() {
return '<label><input type="checkbox" />Task</label>';
},
allday: function(schedule) {
return schedule.title + ' <i class="fa fa-refresh"></i>';
},
alldayTitle: function() {
return 'All Day';
},
time: function(schedule) {
return schedule.title + ' <i class="fa fa-refresh"></i>' + schedule.start;
}
},
month: {
daynames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
startDayOfWeek: 0,
narrowWeekend: true
},
week: {
daynames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
startDayOfWeek: 0,
narrowWeekend: true
}
});
After saving our changes, our calendar becomes viewable.
Displaying Data on the Calendar
Getting Calendar Data
We must define a method that helps us obtain the calendar data. We have already passed the data as json to our index page, so we must parse the data to make it available in our calendar controller. We store this parse information in a variable called schedules. Once we have done that, we iterate over the schedule and create a calendar Schedule that would display each element on the calendar.
getCalendardata(){
var schedules = JSON.parse(document.querySelector("#calendar").dataset.schedules);
window.schedules = schedules;
schedules.forEach(schedule => {
this.calendar.createSchedules([
{
id: schedule.id,
calendarId: '1',
title: schedule.title,
category: 'time',
dueDateClass: schedule.dueDateClass,
location: schedule.location,
start: schedule.start,
end: schedule.end
}
])
});
}
We then call this method underr connect(){} in Our Calendar Controller.
connect() {
this.getCalendardata()
}
Creating Calendar schedules
First we must make some changes to our schedules controller. After a successful update, create or destroy action, we do not want to be redirected. To solve this, we simply comment out several lines from our controller.
def create
@schedule = Schedule.new(schedule_params)
respond_to do |format|
if @schedule.save
# format.html { redirect_to @schedule, notice: "Schedule was successfully created." }
format.json { render :show, status: :created, location: @schedule }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @schedule.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @schedule.update(schedule_params)
# format.html { redirect_to @schedule, notice: "Schedule was successfully updated." }
format.json { render :show, status: :ok, location: @schedule }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @schedule.errors, status: :unprocessable_entity }
end
end
end
def destroy
@schedule.destroy
respond_to do |format|
# format.html { redirect_to schedules_url, notice: "Schedule was successfully destroyed." }
format.json { head :no_content }
end
end
Secondly, we would relax the strong parameters on our controllers a bit.
def schedule_params
params.permit(:title, :calendar_id, :start, :end, :location)
end
To begin display events on the calendar we must first define a javascript method that creates events. We define a method called createCalendarSchedule. Tui provides certain event handlers that we can use to create events. We will use ‘beforeCreateSchedule’ to create a schedule. When a user clicks on the calendar, a popup form is presented. When data is filled in the popup, we collect the information, create a formdata, and submit it via Rails.ajax
reateCalendarSchedule(){
let calendar = this.calendar;
calendar.on('beforeCreateSchedule', function(event) {
var triggerEventName = event.triggerEventName;
var schedule = {
id: 1,
calendarId: '1',
title: event.title,
category: 'time',
location: event.location,
start: event.start,
end: event.end
}
if (triggerEventName === 'click') {
// open writing simple schedule popup
// schedule = {...};
} else if (triggerEventName === 'dblclick') {
// open writing detail schedule popup
// schedule = {...};
}
calendar.createSchedules([schedule]);
let formData = new FormData()
formData.append('title', schedule.title);
formData.append('category', schedule.category);
formData.append('start', schedule.start._date);
formData.append('end', schedule.end._date);
formData.append('location', schedule.location);
Rails.ajax({
type: "POST",
url: '/schedules',
data: formData
})
});
}
We then call this method under connect(){} in Our Calendar Controller.
connect() {
this.getCalendardata()
this.createCalendarSchedule()
}
Updating Calendar Events
We would use another event handler to update calendar schedules. When we click on an already created schedule, a pop would appear that would allow us to edit or delete this schedule. We would use ‘beforeUpdateSchedule’ event to handle submitting our data.
updatedCalendarSchedule(){
let calendar = this.calendar;
calendar.on('beforeUpdateSchedule', function(event) {
var schedule = event.schedule;
var changes = event.changes;
var formUpdate = new FormData()
if (changes.end) {
formUpdate.append("end", changes.end._date)
}
if (changes.start) {
formUpdate.append("start", changes.start._date)
}
if (changes.title) {
formUpdate.append("title", changes.title)
}
if (changes.category) {
formUpdate.append("category", changes.category)
}
calendar.updateSchedule(schedule.id, schedule.calendarId, changes);
Rails.ajax({
type: "PATCH",
url: '/schedules/'+ schedule.id,
data: formUpdate
})
});
}
We create a form with our updated data and submit it via Rails.ajax to ‘/schedules/:id’.
We then call this method under connect(){}
connect() {
this.updatedCalendarSchedule()
this.getCalendardata()
this.createCalendarSchedule()
}
Deleting Calendar Events
We now define a method that uses ‘the BeforeDeleteSchedule’ event handler to delete a schedule entry. This event is called when we click on a schedule on the calendar and click on ‘Delete’. The function makes a request via Rails.ajax to delete with selected schedule based on its ID.
deleteCalendarSchedule(){
let calendar = this.calendar
calendar.on('beforeDeleteSchedule', function(event) {
var schedule = event.schedule;
calendar.deleteSchedule(schedule.id, schedule.calendarId)
Rails.ajax({
type: "DELETE",
url: '/schedules/'+ schedule.id,
})
});
}
We then call this method under connect(){}
connect() {
this.deleteCalendarSchedule()
this.updatedCalendarSchedule()
this.getCalendardata()
this.createCalendarSchedule()
}
README
This README would normally document whatever steps are necessary to get the application up and running.
Things you may want to cover:
-
Ruby version
-
System dependencies
-
Configuration
-
Database creation
-
Database initialization
-
How to run the test suite
-
Services (job queues, cache servers, search engines, etc.)
-
Deployment instructions
-
...
Top comments (0)