【Rails】date_selectタグの使い方メモ

# 最初に

根性でフロント作成〜サーバサイド作成までdate_selectタグで乗り切った！
ので、自分を労わる意味で備忘録を書きます。

間違っているなどあれば、指摘をいただけると嬉しいです。

# date_selectタグ
超簡単に年月日の入力フォームが作れます。
ただ、色々癖があります。

## パターン① 生年月日

[![Image from Gyazo](https://i.gyazo.com/8bdaadaace27699371d0bf899c7edb1b.png)](https://gyazo.com/8bdaadaace27699371d0bf899c7edb1b)


```sample.html.haml

  != sprintf(f.date_select(:birthday, prefix:'birthday',with_css_classes:'XXXXX', prompt:"--",use_month_numbers:true, start_year:Time.now.year, end_year:1900, date_separator:'%s'),'年','月')+'日'

```

## パターン② クレジットカードの有効期限
[![Image from Gyazo](https://i.gyazo.com/cdf8e61962d10205115283bd420d599a.png)](https://gyazo.com/cdf8e61962d10205115283bd420d599a)

```sample.html.haml
      != sprintf(f.date_select(:expiration_date,with_css_classes:'XXXXX', order:[:month,:year,:day], use_month_numbers:true, discard_day:true, start_year:Time.now.year-2000, end_year:Time.now.year-2000+10, date_separator:'%s'),'月') + '年'
```
※payjpに送るときは2000足すか、文字列として頭に'20'を足さないとダメな所が注意です！

# 利用オプションの解説

|オプション|概要|書き方例|
|:--   |:--|:--|
|with_css_class|年月日それぞれでCSSを当てたい場合に利用。実際のCSSはyear,month,dayで指定が可能になる|with_css_classes:'任意のクラス名'|
|order|年月日の順番を変えたい場合に利用。表示させない場合でも必ず年月日全部書く必要あり|order:[:month,:year,:day]|
|use_month_numbers|月の表示を数字にしたい場合に利用|use_month_numbers:true|
|discard_year|年の表示を消したいときに利用|discard_year:true|
|discard_month|月の表示を消したいときに利用|discard_month:true|
|discard_day|日の表示を消したいときに利用|discard_day:true|
|disabled|プルダウンを選択不可にする|disabled:true|
|prompt|指定データがない場合の初期値表示(一番上の表示)に利用|prompt:"--"<br>prompt:"選択してください"|
|selected|指定データがある場合の指定に利用|selected:Time.now(Date型変数ならOK)|
|start_year|プルダウンの一番上に来る年数を指定|start_year:Time.now.year<br>start_year:2019|
|end_year|プルダウンの一番下に来る年数を指定|end_year:Time.now.year-10<br>end_year:1900|
|date_separator|年月日のフォームを分けた場合の中間に表示する値を指定。|date_separator:'%s'<br>date_separator:'/'|

## date_separatorの補足
date_separatorは、フォーム間にのみ文字を挿入してくれます。
ただ、日本語で年月日を指定したい場合はsprintfと組み合わせないと上手く表示ができません。
そこの理解に苦労したので補足。
※yyyy/mm/ddと描きたいだけなら、date_separator:'/'で行ける気がします


【ちょこっと解説】
date_separatorのための記述を抜き出すと以下の感じ。

```sample.html.haml
　!= sprintf(f.date_select(・・・略・・・,date_separator:'%s'),'年','月')+'日'
```

date_separatorを指定した段階では↓の感じになってると考えてます。

```
　　yyyy %s mm %s dd 
```
そこにrubyのsprintf関数を使って記述してあげると、以下の指定をしたみたいになります。

```
   sprintf { yyyy %s mm %s dd } , hash { '年' , '月' }
```

これで%sの部分に後続で指定したStringを%sに埋め込んであげる。という意味になり結果的に「 yyyy 年 mm　月 dd 」となります。
ただ、これだけだと”日"が入らないため、以下のように指定してあげます。

```
   sprintf { yyyy %s mm %s dd } , hash { '年' , '月' } + '日'
```

さらに、hamlの記述が'='のままではString変換されたhtmlがそのまま表示されて酷いことになるので、以下のように'!='指定(明示的にエスケープ処理させる・・・？？)をしてあげます。

```sample.html.haml
  != sprintf { yyyy %s mm %s dd } , hash { '年' , '月' } + '日'

  # ↓ 実際の記述 ↓

  != sprintf(f.date_select('・・・略・・・',date_separator:'%s'),'年','月')+'日'

```

これで何とかなった！！！

## with_css_classの補足
with_css_classで指定したクラスはあまり意味をなしません笑
実際にCSSを記載するときは、以下のクラス名で指定すればCSSが適用されます。

```sample.scss
    .year,.month,.day{
     // 指定したいCSSを記述する
    }
```
確かにブラウザからhtml確認したら、このクラス名が付与されてます。
↓Chromeの検証[Element]で表示させたらこんな感じ。
[![Image from Gyazo](https://i.gyazo.com/c3b1f344e4249547f67a999abb06b18e.png)](https://gyazo.com/c3b1f344e4249547f67a999abb06b18e)
# Controllerへのデータ受け渡し
date_selectタグで入力した値をController側で受け取るときにも一癖あります。
実際のパラメータの中身を見るとよくわかるのですが、該当項目はhashが更にネストしちゃってます。

```
{"user"=>{"birthday"=>{"birthday(1i)"=>"2019", "birthday(2i)"=>"8", "birthday(3i)"=>"29"}}}
```

嫌な予感しかしないですが、この状態で色々試した結果は以下の通り。

```sample.rb

 # OKパターン　→　普通にDate型としてDB登録可能
 def create
   @user = User.new(params[:user][:birthday])
   @user.save
 end

 # NGパターン　→ ストロングパラメータで拾ってこれなくて必須の場合エラー
 def create
   @user = User.new(user_params)
   @user.save
 end

private
 def user_params
   params.require(:user).permit(:birthday)
 end
   
```

個別指定で行けば保存可能なんですが、他の項目がたくさんある場合は一々そんなの指定してられねえ！！！
と思って、苦肉の策で私はこのように記述しました。

```sample.rb

 def create
   params[:user][:birthday] = birthday_join
   @user = User.new(user_params)
 end

private 
  def user_params
    params.require(:user).permit(:birthday)
  end

  def birthday_join
    # パラメータ取得
    date = params[:user][:birthday]

    # ブランク時のエラー回避のため、ブランクだったら何もしない
    if date["birthday(1i)"].empty? && date["birthday(2i)"].empty? && date["birthday(3i)"].empty?
      return
    end

    # 年月日別々できたものを結合して新しいDate型変数を作って返す
    Date.new date["birthday(1i)"].to_i,date["birthday(2i)"].to_i,date["birthday(3i)"].to_i

  end
```
もしかしたら、ストロングパラメータの記述を頑張れば行けるかもですが、それは試してません。
ポイントは「birthday_join」というメソッドです。
ストロングパラメータ取得前に力技でparamsの中身を日付型にしちゃう作戦です。

適切な処理の仕方をご存知の方は、是非ご教示いただきたいです。。。

# まとめ
date_selectタグは超使える子!!
最後まで根気よく仲良くしてあげましょう・・・!!

# 参考サイト：
[「Rails の date_select でつくるセレクトボックスを「年」「月」「日」で区切る」](https://blog.inouetakuya.info/entry/2014/01/31/233000)
　→　erbでの記載例です。こちらを元にしました。
[「date_selectのオプション指定の仕方」](https://qiita.com/Kaisyou/items/c544058dd9c8ae312881)
　→ ここでは紹介していないオプションも紹介されています。
[「Ruby の sprintf で名前付きパラメータ」](http://akishin.hatenablog.jp/entry/20120914/1347582452)
　→ sprintfについては、ここで学びました