Rails Gem simple calendarでのカレンダー実装と条件分岐


概要

現在DMMWEBCAMPというプログラミングスクールで学習しておりまして、
ポートフォリオ作成にあたりイベントテーブルを作成してカレンダーに表示させました。
これから作成する方のお役に立てればと思います。

完成図

参考にさせて頂いた記事

【Rails】Simple Calendarを使ったカレンダー表示
https://qiita.com/mikaku/items/0971442a7308dc669122

github
https://github.com/excid3/simple_calendar

事前準備

イベントテーブル

・data_and_timeがスタートする日時
・meetingfinishtimeが終了する日時

イベント参加テーブル

※投稿いいね機能の延長です。

アソシエーション

models/event.rb
class Event < ApplicationRecord
 has_many :event_participates, dependent: :destroy
end
models/event_participate.rb
class EventParticipate < ApplicationRecord
 belongs_to :event
end

gem install

gem 'simple_calendar', '~>2.0'
$ bundle install

Simple calendarのViewインストール

$ rails g simple_calendar:views

・views/simple_calendar内に3種類のカレンダーの雛形が出来ます。

application.scss(CSS)への記載

stylesheets/applecation.scss
*= require simple_calendar
*= require_tree .
*= require_self

start_time end_timeの定義

models/event.rb
class Event < ApplicationRecord
  has_many :event_participates, dependent: :destroy

  def start_time
    self.date_and_time
  end

  def end_time
    self.meetingfinishtime
  end
end

このgemではstart_timeとend_timeの定義が必要です。(特にstart_time)
作成しているカラムが最初からその名前であれば良いのですが、違うカラム名なのであればモデルの方で定義しましょう。ただし日付と時刻を扱うのでデータ型は恐らくdatetime型でなければならないと思います。
最初定義せずに実装したところエラーが出ました。

githubではscaffoldを使用して最初から上記名称でカラムを作成しておりました。

カレンダーの表示

私のポートフォリオ

views/events/index.html.erb
</div>
        <div class= 'calender'>
            <%= month_calendar events: @events do |date, event| %>
                <%= date %>

                <% event.each do |event| %>
                <br>
                <% if user_signed_in? %>
                    <% if event.user_id == current_user.id %>
                        <i class="fa fa-circle notification-circle" style="color: red;"></i>
                    <% end %>
                <% end %>
                    <% if event.event_participates.where(user_id: current_user).exists? %>
                        <span class="participate">
                          <%= link_to event.title,event_path(event) %>
                        </span>
                    <% else %>
                         <span class="other">
                          <%= link_to event.title,event_path(event) %>
                        </span>
                    <% end %>
                <% end %>
            <% end %>
        </div>

カレンダーの表示とイベントのタイトルだけであれば以下のみで良いです。

<div class= 'calender'>
            <%= month_calendar events: @events do |date, event| %>
                <%= date %>

                <% event.each do |event| %>
                  <%= event.title %>
                <% end %>
            <% end %>
</div>
controllers/events_controller.rb
def index
    @events = Event.all
end

コントローラーで定義しているEvent.allを@eventsに格納しカレンダーで表示しています。
<%= month_calendar events: @events
この構文の中のevents:は恐らく変えるとエラーが出ますが、インスタンス変数は情報が格納されていればどんなものを使用しても大丈夫だと思います。

現在は先ほどインストールしたmonth_calendarを使用していますが、他に2つテンプレートが用意されているので3種類から使用することが出来るようです。

私は参加(participate)アクションを取っていればbackground-colorをオレンジに、
主催しているイベントであれば赤丸のアイコンを付けたかったので以下のような条件分岐を
カレンダー内で行いました。

  <% if event.user_id == current_user.id %>
     <i class="fa fa-circle notification-circle" style="color: red;"></i>
  <% end %>

イベントテーブルの中のuser_id(つまりイベント作成者)が自分であればイベントタイトルの前にfont awesomeのアイコンを表示というものです。インライン要素のものであればここは何でも良いと思います。

次に


 <% if event.event_participates.where(user_id: current_user).exists? %>
      <span class="participate">
        <%= link_to event.title,event_path(event) %>
      </span>
<% else %>
      <span class="other">
        <%= link_to event.title,event_path(event) %>
      </span>
<% end %>

1行目の <% if event.event_participates.where(user_id: current_user).exists? %>で
eventに参加(participete)するユーザーの中に自分(current_user)が存在しているかどうかを探しています。
見つかればclass participate、そうでなければclass otherとすることで、
表示する情報は同じでもclass名が違うという状態にすることが出来ました。

後はCSSで色を変えていくだけなので、皆さんのお好みのようにしていただければと思います。

ちなみにmonth_calenderのviewは以下です。

views/simple_calendar/_month_calendar.html.erb
<div class="simple-calendar">
  <p class="carendar-title"><%= "#{Date.today.month}月のイベントスケジュール" %></p>

  <div class="calendar-heading" data-turbolinks="false">
    <!--%= link_to t('simple_calendar.previous', default: 'Previous'), calendar.url_for_previous_view %-->
    <!-- <span class="calendar-title"><%= t('date.month_names')[start_date.month] %> <%= start_date.year %></span> -->
    <!--%= link_to t('simple_calendar.next', default: 'Next'), calendar.url_for_next_view %-->
    <i class="fa fa-circle notification-circle" style="color: red;"><span>主催イベント</span></i>
    <span class="calendar-info">参加予定</span>
  </div>

  <table class="table table-striped calendar-table">
    <thead>
      <tr>
        <% date_range.slice(0, 7).each do |day| %>
          <th><%= t('date.abbr_day_names')[day.wday] %></th>
        <% end %>
      </tr>
    </thead>

    <tbody>
      <% date_range.each_slice(7) do |week| %>
        <tr>
          <% week.each do |day| %>
            <%= content_tag :td, class: calendar.td_classes_for(day) do %>
              <% if defined?(Haml) && respond_to?(:block_is_haml?) && block_is_haml?(passed_block) %>
                <% capture_haml(day, sorted_events.fetch(day, []), &passed_block) %>
              <% else %>
                <% passed_block.call day, sorted_events.fetch(day, []) %>
              <% end %>
            <% end %>
          <% end %>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>

私のポートフォリオの仕様で上の部分をコメントアウトしています。
コメントアウトの場所に本来であれば次月や前月へのリンクが出てきます。
細かいところであれば柔軟に変えれそうです。

まとめ

最初はfullcalendarでの実装を目論んだのですが、理解が及ばず諦めて今回こちらのgemを使用しました。イベントタイトルがstart_timeとend_timeの間で毎回出てしまうことを改善できればと思ったのですが私の力不足で難しかったです。
要素のclass名に特に意味はありません。スペルを所々間違えてますが、、
このぐらい簡単なカレンダーでも見せ方次第で私のものよりもっといいものになると思うので是非やってみてください。