【Rails6】flatpickrのカレンダーを使ってDBにパラメータを保存する方法

**flatpickrのカレンダーを使ってDBにパラメータを保存する方法**を解説します。

flatpickrのカレンダーを使ってみたけど、DBにパラメータを保存できない。
視覚的にわかりやすいカレンダータイプの日付選択を採用したい。
カレンダーからポチポチして日付を選べるようにしたい。

など、ポートフォリオ作成などにも役立つと思い記事にしてみました。
## 完成後のアプリ
[![Image from Gyazo](https://i.gyazo.com/ab81088aed4f7ae3249a01db5279e32d.gif)](https://gyazo.com/ab81088aed4f7ae3249a01db5279e32d)

## 開発環境
・macOS Catalina 10.15.7
・Ruby 2.7.2
・Rails 6.0.3.4
・flatpickr 4.6.9

### 1. 準備
 - 以下を前提に進行
   - トップページはcalendars#indexの前提で進行
   - データベースはPosgreSQL

#### 1.1 アプリの作成とgemの追加
 
```html:ターミナル
rails new flatpickr_sample -d postgresql
cd flatpickr_sample
```
- 今回アプリ名はflatpickr_sampleとします。
- アプリ作成後はcdコマンドで該当アプリのディレクトリに移動することを忘れないようにしましょう。

```ruby:Gemfile
# 中略
gem 'bootsnap', '>= 1.1.0', require: false

# ***** 以下を追加 *****
# 日本語化
gem 'rails-i18n', '~> 6.0'
# ***** 以上を追加 *****

# 中略

group :development do
  # 中略

  # ***** 以下を追加 *****
  # デバッグで利用
  gem 'pry-byebug'
  # ***** 以上を追加 *****

end
```

```terminal:ターミナル
bundle install
rails db:create
```
- gemの追加後は忘れずbundle installを実行
- その後rails db:createでデータベースを作成

【要確認】ターミナルにてrails sでサーバーを起動して、http://localhost:3000/ に接続して「Yay! You’re on Rails!」が出るか確認しましょう。

--- 

#### 1.2 モデルとテーブルの作成
```terminal:ターミナル
rails g model Calendar date:date
rails db:migrate
```
- 今回モデル名はCalendar、カラムはdateカラムを使用します。
- rails db:migrateによりテーブルが作成されます。

【要確認】上記コマンドによりapp/models/calendar.rbファイル、db/migrate/..._create_calendars.rbファイル、db/schema.rbファイルが作成されています。

---
#### 1.3 コントローラとビューファイルの作成
```terminal:ターミナル
rails g controller calendars index new create
```
- コントローラ名はcalendarsとします。コントローラ名は原則複数形なので注意しましょう。
- rails g controller calendars(コントローラ名) index(コントローラアクション名)で、アクション名のビューファイルも一緒に作成されます。

```terminal:ターミナル
rm -f app/views/calendars/create.html.erb
```
- 今回使用しない不要なファイルをrm -fコマンドで削除しましょう。

---

### 2 flatpickrの導入
#### 2.1 flatpickrのインストール
``` terminal:ターミナル
yarn add flatpickr
```
- yarnを使用してflatpickrを導入します。
- yarnが使用できない場合、先にyarnのインストールをしましょう。


####2.2 flatpickrの読み込み
```scss:app/assets/stylesheets/application.scss
/* ...
 *= require_tree .
 *= require_self

# ***** 以下を追加 *****
 *= require flatpickr
# ***** 以上を追加 *****

 */
```
【注意】app/assets/stylesheets/application.cssの拡張子cssをscssに変更するのを忘れずに

```javascript:app/javascript/packs/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
// ***** 以下を追加 *****
require('flatpickr')
require('flatpickr/dist/l10n/ja')
// ***** 以上を追加 *****
```
####2.3 calendar.jsファイルの作成と読み込み
```js:app/javascript/packs/calendar.js
// ***** 以下を追加 *****
 document.addEventListener("turbolinks:load", () => {
  flatpickr.localize(flatpickr.l10ns.ja)
  const config = {
    inline: true,
    disableMobile: true
  }
  flatpickr('#calendar_form', config);
});
// ***** 以上を追加 *****
```
- app/javascript/packsディレクトリ内にcalendar.jsファイルを作成する。
- 今回id名はcalendar_formとします。上記の5行目の#以降がid名となります。

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>FlatpickrSample</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<!-- ***** 以下を追加 ***** -->
    <%= javascript_pack_tag 'calendar', 'data-turbolinks-track': 'reload' if controller_name == 'calendars' %>
<!-- ***** 以上を追加 ***** -->

  </head>
  <body>
    <%= yield %>
  </body>
</html>
```

- 上記の追加は、コントローラ名がcalendarsと時のみ、packs内のcalendar.jsに対してターボリンクスをするという記述になります。

---
###3 CRUD処理
####3.1 ルーティング設定
```ruby:config/routes.rb
Rails.application.routes.draw do
  root "calendars#index"
  resource :calendars, only: [:index, :new, :create]
end
```
####3.2 カレンダー投稿ページの作成(new)と一覧ページの作成(index)
```html:app/views/calendars/new.html.erb
<!-- ***** 以下を追加 ***** -->
<%= form_with model: @calendar do |form| %>
  <input id ="calendar_form" type="text" name="calendar[date]" readonly="readonly" >
  <%= form.submit "送信" %>
<% end %>
<!-- ***** 以上を追加 ***** -->
```
- dateカラムのパラメータを保存するためにヘルパーメソッドのform_withを使用します。
- app/javascript/packs/calendar.js内で設定したid名と、上記２行目のinput id ="id名"を一致させることでflatpickrのカレンダーを呼び出します。

```html:app/views/calendars/index.html.erb
<!-- ***** 以下を追加 ***** -->
<%= link_to "日付投稿ページへ", new_calendars_path %>
<h1>日付一覧</h1>
<% @calendars.each do |calendar| %>
  <%= calendar.date %>
<% end %>
<!-- ***** 以上を追加 ***** -->
```
・上記はカレンダーで送信されたパラメータを一覧表示するための記述です。

---
#### 3.3 コントローラのアクション作成(index,new,create)

```ruby:app/controllers/calendars_controller.rb
class CalendarsController < ApplicationController
  def index
  # ***** 以下を追加 *****
    @calendars = Calendar.order(id: :asc)
  # ***** 以上を追加 *****
  end

  def new
  # ***** 以下を追加 *****
    @calendar = Calendar.new
  # ***** 以上を追加 *****
  end

  def create
  # ***** 以下を追加 *****
    post = Calendar.create!(params[:date])
    redirect_to root_path
  # ***** 以上を追加 *****
  end
  
end
```

- 結論から言うと、上記のコードを追加してもパラメータがDBへ保存されません。なぜ保存できないのかを次に詳しく説明します。

---
###4 なぜ保存できないかを調べてみる
#### 4.1 binding.pryを使って検証してみる
```ruby:app/controllers/calendars_controller.rb
class CalendarsController < ApplicationController
  def index
    @calendars = Calendar.order(id: :asc)
  end

  def new
    @calendar = Calendar.new
  end

  def create
  # ***** 以下を追加 *****
    binding.pry
  # ***** 以上を追加 *****
    post = Calendar.create!(params[:date])
    redirect_to root_path
  end

end
```
【確認手順】
1. binding.pryを上記の位置に追加する。
2. ターミナルにてrails sでサーバーを起動して、http://localhost:3000/ に接続する。
3. カレンダーから適当な日付を選択して送信する。
4. 下記のように処理が止まったのを確認し、paramsと入力する。送信されたパラメータの中身を確認できます。

---
####4.2 カレンダーフォームから送られたパラメータの中身
```terminal:ターミナル
    10: def create
    11:   binding.pry
 => 12:   save_params = params[:calendar][:date].to_date
    13:   Calendar.create!(date: save_params)
    14:   redirect_to root_path
    15: end

[1] pry(#<CalendarsController>)> params
=> <ActionController::Parameters {"authenticity_token"=>"0E0nP7UnRkkg+4ZL2h7vZ160OkJP0NAQEkNaYF4nAC5+KH6JKSOKkN6rMB0y4eMthFRZIYGtBHHOLIOTCf5ekg==", "calendar"=>{"date"=>"2021-03-09"}, "commit"=>"送信", "controller"=>"calendars", "action"=>"create"} permitted: false>
[2] pry(#<CalendarsController>)>
```

- 上記の青字のコードがカレンダーから送られたパラメータになります。不要な部分が多いことがわかります。必要な部分は"calendar"=>{"date"=>"2021-03-09"}部分のみになります。

---
#### 4.3 パラメータをDBに保存できるようにするには
```terminal:ターミナル
[2] pry(#<CalendarsController>)> params[:calendar]
=> <ActionController::Parameters {"date"=>"2021-03-09"} permitted: false>
```
- params[:calendar]と入力することで青字の"date"=>"2021-03-09"が抽出できました。ですが、まだ保存できる形ではありません。

```terminal:ターミナル
[4] pry(#<CalendarsController>)> params[:calendar][:date]
=> "2021-03-09"
```
- params[:calendar][:date]と入力することで青字の"2021-03-09"が抽出できました。これで保存されそうな気がしますが、これでは文字列扱いなので保存ができません。

```terminal:ターミナル
[6] pry(#<CalendarsController>)> params[:calendar][:date].to_date
=> Wed, 03 Mar 2021
```
- params[:calendar][:date].to_dateとすることで=> Wed, 03 Mar 2021という結果となりました。to_dateメソッドを使うことでdateカラムに保存されるようにうまいこと変換してくれます。

```ruby:
save_params = params[:calendar][:date].to_date
Calendar.create!(date: save_params)
```

- 最後にparams[:calendar][:date].to_dateを変数save_paramsにして、Calendar.create!(date: save_params)でdateカラムに該当する日付を保存します。

---
### 5 まとめ
```ruby:app/controllers/calendars_controller.rb
class CalendarsController < ApplicationController
  def index
    @calendars = Calendar.order(id: :asc)
  end

  def new
    @calendar = Calendar.new
  end

  def create
    save_params = params[:calendar][:date].to_date
    Calendar.create!(date: save_params)
    redirect_to root_path
  end

end
```
結果、コントローラに上記のように書くことでflatpickrのカレンダーからのパラメータをDBに保存することができました。

この記事ではjavascriptライブラリのflatpickrを使ったカレンダーでの日付投稿機能の実装を行いました。初めてのQuiit投稿なので上手く伝わるように書けたかわかりませんが、見てくれた方の役に立てれば嬉しいです。

今後は、今回とは逆にDBの日付データをflatpickrのカレンダーに表示させたり、flatpickrのカレンダーにbootstrapのスタイルを利用などについて書きたいと思います。


## 最終的なコード
- GitHubに完成後のコードをアップしております
  - https://github.com/yasu0408/flatpickr_sample.git

 ```
$ git clone https://github.com/yasu0408/flatpickr_sample.git
```