[Rails]FullCalenderでカレンダー機能の実装

## 実装すること
①マイページで予定管理ができるようにする
②日付をクリックで予定の作成・編集をできるようにする
※Userモデル等は作成した前提で進めていきます。

## FullCalenderとは
オープンソースのカスタマイズ可能なjqueryのカレンダーライブラリです。
上手く活用することで、カレンダーのスクラッチ開発をせずにリッチなカレンダーを表示できます。

## ER図

<img width="554" alt="calenderER図.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539971/ed96da21-982d-82b6-6e1e-27ddf084e82c.png">

## モデルの作成

```
$ rails g model Event title:string body:text disp_flg:boolean start:datetime end:datetime allDay:string
```

## アソシエーションの確認

```app/models/user.rb
class User < ApplicationRecord

    has_many :events

end
```

```app/models/event.rb
class Event < ApplicationRecord

    belongs_to :user

end
```

## ダウンロード
FullCalenderの公式ドキュメントからダウンロードし、zipを解凍します。
https://fullcalendar.io/

## Gemの追加

```Gemfile.
gem 'jquery-rails', '4.3.3'
gem 'fullcalendar-rails'
gem 'momentjs-rails'
```

```
$ bundle inatall
```

## 読み込み
application.htmlのhead内で読み込みます。

```app/views/layouts/application.html
<head>
~
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/4.2.0/core/main.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/4.2.0/daygrid/main.min.css">
~
</head>
```

```app/assets/javascripts/application.js
//= require jquery
//= require moment
//= require fullcalendar
```


## 表示する(マイページ)
<img width="531" alt="カレンダー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539971/3a623adc-d50e-72d9-e3f5-bca7f7bfa9b8.png">

<img width="508" alt="カレンダー登録.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539971/ac91a317-1001-ee58-03c5-53385de92a2b.png">

### コントローラー

```app/contorollers/users_contoroller.rb
  def show
    @user = User.find(params[:id])
    @events = Event.where(user_id: @user.id)
    @event = Event.new
  end
```

```app/controllers/events_controller.rb
    def create
	    event = Event.new(event_params)
        event.save!
	    @events = Event.where(user_id: current_user.id)
	end

	def update
	  	event = Event.find(params[:id])
	  	@events = Event.where(user_id: current_user.id)
        event.update(event_params)
    end

    def destroy
  	    @user = User.find(params[:id])
  	    event = Event.find(params[:id])
  	    event.destroy
  	    redirect_to user_path(@user)
    end

	private
	def event_params
	    params.require(:event).permit(:title, :start, :end, :user_id, :body)
	end
```

### ビュー
#### 予定を表示する・作成する

・`<div id="calendar"></div>`でカレンダーを表示しています。
・予定を編集するときのモーダル内容はパーシャルにします。

```app/views/users/show.html

<h3 class="text-center">カレンダー</h3>
<!-- カレンダーの表示 -->
<div id="calendar"></div>
<!-- もしユーザーがログインしていたら -->
<% if user_signed_in? %>
    <!-- ページのparams.idがログインユーザー.idと同じなら -->
	<% if @user.id == current_user.id %>
		<div id="inputScheduleForm" class="modal fade" tabindex="-1">
			<div class="modal-dialog modal-nm">
				<div class="modal-content">
					<div class="modal-header">
						<h4 class="modal-title">スケジュール登録</h4>
					</div>
					<div class="modal-body">
						<%= form_with model: @event, url: events_path do |f| %>
							<div  class="col">
									<p>
										<span>タイトル</span>
										<span><%= f.text_field :title, class: "form-control", placeholder: "タイトルを入力してください" %></span>
									</p>
									<span>開始日時</span>
									<span><%= f.datetime_field  :start, placeholder: "XXXX-XX-XX", class:"field" %> ~ </span><br>
									<span>終了日時</span>
									<span><%= f.datetime_field :end, placeholder: "XXXX-XX-XX", class:"field" %></span><br>
									<span>詳細
									<%= f.text_field :body, class: "form-control", placeholder: "タイトルの詳細を記入してください" %></span>
							</div>
							<div class="modal-footer">
								<%= f.submit "登録する", class: "btn btn-primary" %>
								<%= f.hidden_field :user_id, :value => current_user.id %>
								<button type="button" class="btn btn-default" data-dismiss="modal">閉じる</button>
							</div>
						<% end %>
					</div>
				</div>
			</div>
		</div>
<!-- 編集 ------------------------------------------------------------------>
		<div id="inputEditForm">
			<%= render 'events/edit', events: @events %>
		</div>
	<% end %>
<% end %>
	<script>
	// CRUDを行う際にCSRF対策のtokenを発行
		$(document).ready(function() {
	    var prepare = function(options, originalOptions, jqXHR) {
	      var token;
	      if (!options.crossDomain) {
	        token = $('meta[name="csrf-token"]').attr('content');
	        if (token) {
	          return jqXHR.setRequestHeader('X-CSRF-Token', token);
	        }
	      }
	    };
	    }
	    )

	    // カレンダー表示
		$('#calendar').fullCalendar ({
			header: {
		        left: 'prev,next today',
		        center: 'month,agendaWeek,agendaDay',
		        right: 'title'
		    },

		    buttonText: {
			      prev: "<",
			      next: ">"
			},

			timezone: 'UTC',
	      	events: '/users/events.json',
	      	navLinks: true,
	      	selectable: true,
	      	selectHelper: true,
	      	// 日付クリック
			dayClick : function ( date , jsEvent , view ) {
				$('#inputScheduleForm').modal('show');
				},

			// event クリックで編集、削除
			eventClick : function(event, jsEvent , view) {
	    		jsEvent.preventDefault();
				$(`#inputScheduleEditForm${event.id}`).modal('show');
	  		},

	  		eventMouseover : function(event, jsEvent , view) {
	    		jsEvent.preventDefault();
	  		}
		})

	</script>

```

```app/views/events/create.js
//フォームを空にする
$('input[type="text"]').val('');
//モーダルを消す
$('#inputScheduleForm').modal('hide');
//作成した予定を差し替える
$('#inputEditForm').html('<%= escape_javascript(render("events/edit", events: @events )) %>');
// フルカレンダー を一度消しもう一度表示
$('#calendar').fullCalendar('refetchEvents')

```

### 予定を編集する

```app/views/events/_edit.html
<% events.each do |event| %>
	<div id='inputScheduleEditForm<%= event.id %>' class="modal fade" tabindex="-1">
		<div class="modal-dialog modal-nm">
			<div class="modal-content">
				<div class="modal-header">
					<h4 class="modal-title">スケジュール編集</h4>
				</div>
				<div class="modal-body">
					<div  class="col">
						<%= form_with(model: event, url: users_event_path(event), method: :put) do |f| %>
							<p>
								<span>タイトル</span>
								<span id="inputTitle" value=""><%= f.text_field :title, class: "form-control", placeholder: "タイトルを入力してください" %></span>
							</p>
							<span>開始日時</span>
							<span><%= f.datetime_field  :start, placeholder: "XXXX-XX-XX", class:"field" %> ~ </span><br>
							<span>終了日時</span>
							<span><%= f.datetime_field  :end, placeholder: "XXXX-XX-XX", class:"field" %></span>
							<span><br>
								詳細
								<%= f.text_field :body, class: "form-control", placeholder: "タイトルの詳細を記入してください" %>
							</span>
							<div class="modal-footer">
								<%= f.hidden_field :user_id, :value => current_user.id %>
								<%= f.submit "編集する", class: "btn btn-primary" %>
								<button type="button" class="btn btn-default" data-dismiss="modal">閉じる</button>
							</div>
						<% end %>
					</div>
				</div>
			</div>
		</div>
	</div>
<% end %>
```

```app/views/events/update.js
//updateした内容を差し替え
$('#inputEditForm').html('<%= escape_javascript(render("events/edit", events: @events)) %>');
//モーダル背景画面を消す
$('.modal-backdrop').remove();
// フルカレンダー を一度消しもう一度表示
$('#calendar').fullCalendar('refetchEvents')
```

## 最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。


## 参考
公式ドキュメント
https://fullcalendar.io/
[Rails]FullcalendarのイベントをDBに保存・編集
https://qiita.com/ShoutaWATANABE/items/3d0cddafadb4f275991e