はじめに
こんにちは!
Materializeには便利なコンポーネントがたくさんあります。
DatePickerやTimePickerもその一つです。
ただ、現状DateTimePickerはないようです。
Railsで日付+時間で操作したいこともあるかと思います。
たまたま要件として、フォームの中でDateTimeを扱う必要がでました。
私が考えた選択肢としては、
- DateTimePickerを作る or 別のプラグインを入れる
- RailsのFormヘルパーを使用する(その部分だけデザインが変わることを許容する)
- DatePickerとTimePickerをそれぞれ使用し、DateTimeに変換する
今回は極力Materializeのフレームワークに載ったかたちで進めたい、ということもあったので、「3」を選択しました。
その他の選択肢もあるかと思います。
今回は参考になるかと思い、記事にまとめたいと思います。
前提条件
今回はイベントを管理する
events
テーブルに、開始日時を保存する
started_at
があるとします。
started_at
は
datetime
でDBに保存するものとします。
ビューのイメージ
上述の通り、MaterializeにDateTimePickerがないので、下記のように入力フォームを分けたいと思います。
モデルにゲッターとセッターを追加
started_at
とは別で、
started_at_h
というメソッドを追加します。
この
started_at_h
に値が入ったら、
started_at
にDateTimeでセットします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Event < ApplicationRecord # ... def started_at_h=(values) self.started_at = if values.is_a?(Hash) && values.key?('date') && values.key?('time') "#{values['date']} #{values['time']}" else Time.zone.now end end def started_at_h if started_at.present? { 'date' => started_at.strftime('%Y-%m-%d'), 'time' => started_at.strftime('%H-%M') } else { 'date' => '', 'time' => '' } end end end |
started_at_h
にはハッシュで
date
と
time
を渡し、その値を使って
started_at
を更新します。
逆に取り出す時は、
started_at
の値を操作します。
入力フォームに追加
JSファイルにて使用宣言
MaterializeのDatePickerとTimePickerを使用するためには、JSにて宣言する必要があります。
1 2 3 4 5 6 | // DatePicker var elem = document.querySelector('.datepicker'); var instance = M.Datepicker.init(elem, options); // TimePicker var elem = document.querySelector('.timepicker'); var instance = M.Timepicker.init(elem, options); |
JQueryを使用している場合は、下記のようになります。
1 2 3 4 | $(document).ready(function(){ $('.datepicker').datepicker(); $('.timepicker').timepicker(); }); |
HTML
フォームの方では、
strated_at_h
のnameで渡します。
nameは
started_at_h[date]
といったかたちで書くことで、submitした後ハッシュでパラメータを投げてくれます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <%= form_with(model: @event, local: true) do |form| %> <!-- ... --> <div class="row"> <div class="col s6 m6"> <%= form.label :started_at, '開始日' %> <%= form.text_field 'started_at_h[date]', { class: 'datepicker', value: @event.started_at_h['date'] } %> </div> <div class="col s6 m6"> <%= form.label :started_at, '開始時間', class: 'required' %> <%= form.text_field 'started_at_h[time]', { class: 'timepicker', value: @event.started_at_h['time']} %> </div> </div> <div class="row"> <%= form.submit %> </div> <% end %> |
コントローラーのストロングパラメータを修正
フォームから値を受け取る時は、スタロングパラメータを使用することが多いと思います。
受け取る際には、
started_at
ではなく、
started_at_h
にし、ハッシュを指定します。
1 2 3 4 5 6 | def event_params params.fetch(:event, {}).permit( # ... { started_at_h: [] }, ) end |
テストコードも書いてみる
モデルの
started_at_h
についてテストコードも書きました。
こちらもこれで良いのかよく分からないので参考程度にご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | require 'rails_helper' RSpec.describe Event, type: :model do describe '#started_at_h' do let(:event) { FactoryGirl.build(:event) } context 'when set params' do it 'ハッシュが渡されればその日付のDateTimeが返る' do params = { 'date' => '2018-01-14', 'time' => '10:06' } event.started_at_h = params expect(event.started_at.strftime('%Y-%m-%d %H-%M')).to eq DateTime.new(2018, 1, 14, 10, 6).strftime('%Y-%m-%d %H-%M') end it 'ハッシュが渡されなければ本日のDateTimeが返る' do params = {} event.started_at_h = params # 時間がずれてテストがコケる可能性があるので日付だけ比較 expect(event.started_at.strftime('%Y-%m-%d')).to eq Time.zone.now.strftime('%Y-%m-%d') end end context 'when get params' do it '対象の日付が空であれば、空で返る' do event.started_at = '' expect_param = { 'date' => '', 'time' => '' } expect(event.started_at_h).to eq expect_param end it 'すでに値がセットされていれば、その日付のハッシュが返る' do time = Time.zone.now event.started_at = time expect_param = { 'date' => time.strftime('%Y-%m-%d'), 'time' => time.strftime('%H-%M') } expect(event.started_at_h).to eq expect_param end end end end |
さいごに
今回の件に対する良い対応なのかは分かりませんが、一例としてご覧いただければ嬉しく思います。
また、もし他に良い方法があればご教授いただきたく思います。