最初に
根性でフロント作成〜サーバサイド作成までdate_selectタグで乗り切った!
ので、自分を労わる意味で備忘録を書きます。
間違っているなどあれば、指摘をいただけると嬉しいです。
date_selectタグ
超簡単に年月日の入力フォームが作れます。
ただ、色々癖があります。
パターン① 生年月日
!= 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'),'年','月')+'日'
パターン② クレジットカードの有効期限
!= 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:"--" prompt:"選択してください" |
selected | 指定データがある場合の指定に利用 | selected:Time.now(Date型変数ならOK) |
start_year | プルダウンの一番上に来る年数を指定 | start_year:Time.now.year start_year:2019 |
end_year | プルダウンの一番下に来る年数を指定 | end_year:Time.now.year-10 end_year:1900 |
date_separator | 年月日のフォームを分けた場合の中間に表示する値を指定。 | date_separator:'%s' date_separator:'/' |
date_separatorの補足
date_separatorは、フォーム間にのみ文字を挿入してくれます。
ただ、日本語で年月日を指定したい場合はsprintfと組み合わせないと上手く表示ができません。
そこの理解に苦労したので補足。
※yyyy/mm/ddと描きたいだけなら、date_separator:'/'で行ける気がします
【ちょこっと解説】
date_separatorのための記述を抜き出すと以下の感じ。
!= 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がそのまま表示されて酷いことになるので、以下のように'!='指定(明示的にエスケープ処理させる・・・??)をしてあげます。
!= sprintf { yyyy %s mm %s dd } , hash { '年' , '月' } + '日'
# ↓ 実際の記述 ↓
!= sprintf(f.date_select('・・・略・・・',date_separator:'%s'),'年','月')+'日'
これで何とかなった!!!
with_css_classの補足
with_css_classで指定したクラスはあまり意味をなしません笑
実際にCSSを記載するときは、以下のクラス名で指定すればCSSが適用されます。
.year,.month,.day{
// 指定したいCSSを記述する
}
確かにブラウザからhtml確認したら、このクラス名が付与されてます。
↓Chromeの検証[Element]で表示させたらこんな感じ。
Controllerへのデータ受け渡し
date_selectタグで入力した値をController側で受け取るときにも一癖あります。
実際のパラメータの中身を見るとよくわかるのですが、該当項目はhashが更にネストしちゃってます。
{"user"=>{"birthday"=>{"birthday(1i)"=>"2019", "birthday(2i)"=>"8", "birthday(3i)"=>"29"}}}
嫌な予感しかしないですが、この状態で色々試した結果は以下の通り。
# 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
個別指定で行けば保存可能なんですが、他の項目がたくさんある場合は一々そんなの指定してられねえ!!!
と思って、苦肉の策で私はこのように記述しました。
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 でつくるセレクトボックスを「年」「月」「日」で区切る」
→ erbでの記載例です。こちらを元にしました。
「date_selectのオプション指定の仕方」
→ ここでは紹介していないオプションも紹介されています。
「Ruby の sprintf で名前付きパラメータ」
→ sprintfについては、ここで学びました