今回はrailsで期間をセレクトボックスで登録していきます。いや、input type=monthがあるじゃんとツッコミがきそうですが、あまりデザイン的にカッコよくなかったのと変えようと思うとjavascriptを結構頑張らないといけないのでセレクトボックスで選択できるようにしました。
今回作るもの
下記のものを作っていきます。
- rails
- 期間を開始年月から終了年月で入力
- データの方はdate型
- 開始年月は終了年月よりも前でないと弾く
- 年月がちゃんとペアで入っていないといけない
メリデメ
input type=”month”を使う
通常で考えると年月をdate型で登録したいという場合はinput type=”month”を使う。

https://developer.mozilla.org/ja/docs/Web/HTML/Element/input/month
ただ、あまりデザイン的に好きじゃないのとデザインを変えようと思うとjavascriptを結構頑張らないといけない。デザイン気にしない場合はこれを使うのが最もバグが少なく、楽。
セレクトボックスを使う
セレクトボックスで登録する場合、下記が面倒。
- 年月どちらかけた際の挙動を指定
- 年月を別のセレクトボックスでparamsを送り、date型に変換し、繋げないといけない
上記デメリットがあるがデザインを変更できる方を優先したい場合はセレクトボックス。
実装
formの実装
<%= form_with model: @career, local: true do |form| %>
<div>
<label class="form_label">
期間
</label>
<div class="flex">
<div class="mb-4 form_period flex">
<div class="select year_select">
<%= form.select :period_start_year, options_for_select(((Time.now.year - 50)..Time.now.year).reverse_each), include_blank: "年" %>
<div class="select__arrow"></div>
</div>
<div class="select month_select">
<%= form.select :period_start_month, [["1月","01"],["2月","02"],["3月","03"],["4月","04"],["5月","05"],["6月","06"],["7月","07"],["8月","08"],["9月","09"],["10月","10"],["11月","11"],["12月","12"]], include_blank: "月" %>
<div class="select__arrow"></div>
</div>
</div>
<div class="mb-8 form_period flex">
<div class="form_period_end_text">
〜
</div>
<div class="select year_select">
<%= form.select :period_end_year, options_for_select(((Time.now.year - 50)..Time.now.year).reverse_each), include_blank: "年" %>
<div class="select__arrow"></div>
</div>
<div class="select month_select">
<%= form.select :period_end_month, [["1月","01"],["2月","02"],["3月","03"],["4月","04"],["5月","05"],["6月","06"],["7月","07"],["8月","08"],["9月","09"],["10月","10"],["11月","11"],["12月","12"]], include_blank: "月" %>
<div class="select__arrow"></div>
</div>
</div>
</div>
</div>
<div class="overflow-hidden">
<%= form.submit "登録" %>
</div>
<% end %>
selectタグで生成する。年を一つ一つオプション書くのは面倒なので
<%= form.select :period_end_year, options_for_select(((Time.now.year - 50)..Time.now.year).reverse_each), include_blank: "年" %>
上記で50年分生成する。
include_blankで初期値を設定。
controller1:年月揃ったら登録
年だけ入ってて月が入っていないのに入力されると面倒なので下記で弾く
if params[:career][:period_start_year].present? && params[:career][:period_start_month].present? && params[:career][:period_end_year].present? && params[:career][:period_end_month].present?
period_start = Date.new(params[:career][:period_start_year].to_i, params[:career][:period_start_month].to_i)
period_end = Date.new(params[:career][:period_end_year].to_i, params[:career][:period_end_month].to_i)
else
flash[:alert] = "開始、完了の年月を全て入力してください。"
redirect_back(fallback_location: root_path) and return
end
開始だけ、完了だけも困るので全部揃った場合だけ登録できるようにする。modelで弾くかcontrollerで弾くかは難しいところだが、date型への変更の前に弾いた方が判定が楽だったので今回はcontrollerで弾く。
period_start = Date.new(params[:career][:period_start_year].to_i, params[:career][:period_start_month].to_i)
上記でdate型に変換。というよりdate型をnewで生成する。
気をつけないといけないのが、こういったcontrollerで弾きたい場合にredirectをかけるなら下記のようにand returnを書かないとダブルレンダリングになる。これはredirectが予約されている状態になるので後ろにさらにredirectを書いていると二重になってしまうため。
redirect_back(fallback_location: root_path) and return
controller2:開始年月が終了年月よりも後ろだと弾く
開始年月が終了年月よりも後ろだと弾く処理はcontrollerでのdate型への変換と関係ないのでmodelで弾く。下記をmodelに記述。
validate :period_end_after_period_start
private
def period_end_after_period_start
if period_end.present? && period_start.present? && period_end < period_start
errors.add(:base, "終了月は開始月より後にしてください")
end
end
完成
上記でセレクトボックスの開始年月、完了年月の入力が完了。