その他
    ホーム 技術発信 DoRuby datetimepickerでの、あり得ない日付の入力制御の覚え書き

    datetimepickerでの、あり得ない日付の入力制御の覚え書き

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    railsのビューで、フォームを使って日付を入力させたい時、

    datetimepickerを使っていました。

    datetimepickerはJSでテキストフィールドに付加する事で、カレンダー表示で日付・時間を選択させる事のできる便利なプラグインです。

    ただ、テキストエリアに追加するので、ユーザーがテキストフィールドに直接日付を入力する事も出来てしまいます。

    それ自体は別に問題ない事ですが、日付じゃないテキストや、あり得ない日付を入力されてしまう場合もあります。

    日付じゃないテキストについては、datetimepickerが勝手に判断してテキストフィールドの中を空に置き換えてくれるのですが、

    あり得ない日付に関しては、

    「2000-04-31」-> 「2000-05-01」に変換してくれる

    「2000-05-32」-> 変換してくれない!

    となるので、フォームでもらった値を、

    モデルインスタンスのDateTime型のカラムに代入しようとするとエラーを吐きます。

    @model = Model.new(date: param[:model][:date])
     -> ArgumentError
    

    どうやら31日までは,datetimepicker側で勝手に判断して変換してくれるようですが、

    32日を超えると「ありえない日付」となり、変換はしてくれないようです。

    これでは困るので、色々と解消しようとした顛末です。

     1. validationで弾けないか?

    モデルにvalidateを設置して、フォームを送ったら「不正な値です」などのアラートを表示させたいと思いましたが、

    よくよく考えると、validate発火前の、インスタンスへの代入時点でエラーになっているので、ちょっと厳しそうです。

    コントローラの方でチェックするのも考えましたが、フォームを使うコントローラ全てにチェックを入れないといけなかったりするので、

    あまり良くなさそうです。

     2. JSで弾く(onBlur編)

    そもそもそのような入力が出来なければ良い話なので、JSで入力制御を入れる事にしました。

    ちなみに、datetimepicker導入部のJSはこんな感じ。

    $('.date_text_field').datetimepicker({
      format: 'YYYY-MM-DD',
      language: 'ja'
    });
    

    今回は日付だけ取りたいので、時間までは取らないようにしています。

    このままだと入力制御がかかってないので、onBlurのイベントハンドラを使ってみます。

    $('.date_text_field').datetimepicker({
      format: 'YYYY-MM-DD',
      language: 'ja'
    });
    
    $('.date_text_field').onBlur = function(){
      if ($(this).val() != '') {
    	  var inputDate = new Date($(this).val());
    	  if (isNaN(inputDate.getTime())) {
    	    $(this).val('');
    	  }
      }	
    }
    

    「isNaN(inputDate.getTime())」でテキストフィールドに入力された値が日付として正当か判断しています。

    これでテキストフィールドに「2000-03-34」のようなおかしい日付を入力して、フォーム送信ボタンを押すと・・・・

    テキストフィールドの中身が空になりました!

    ・・・しかし、onBlurを使っているので、テキストフィールドからフォーカスが外れれば空になってくれるのですが、

    テキストフィールドに入力した状態で、エンターキーを押してそのままフォーム送信したりすると、、

    フォーカスは外れないので、そのまま値が送られてしまい、やはりArgumentErrorになってしまいました。

     3. JSで弾く(onsubmit編)

    ならば、フォーム全体がsubmitされたタイミングで入力判定すれば良いじゃないか!

    という訳で、フォーム全体のイベント監視をさせるようにしてみます。

    $('.date_text_field').datetimepicker({
      format: 'YYYY-MM-DD',
      language: 'ja'
    });
    
    $('form').on('submit', function(){
      picker();
     });
    
    function picker(){
      $('.date_text_field').each(function(){
        if ($(this).val() != '') {
          var inputDate = new Date($(this).val());
          if (isNaN(inputDate.getTime())) {
            $(this).val('');
          }
        }
      });
    }
    

    formタグを監視するようにしてみました。

    フォームがsubmitされると、date_text_fieldクラスを探し出し、日付として不正の場合値を空にします。

    これで再度日付入力用のテキストフィールドに「2000-03-34」を入力してフォーム送信・・・

    -> 送信直前にフォームの内容が空になりました!

     4. JSで弾く(解決編)

    上記の方法で良いかと思ったのですが、

    一画面に複数formタグがあったときは、そもそもformにJS監視させたくないときは?などの疑問が残ります。

    そんな中、とてもスマートな解決法を教えて頂きました。

    $('.picker_date').datetimepicker({
      format: 'YYYY-MM-DD',
      language: 'ja'
    }).on('dp.error', function(e) {
      $(e.target).val('');
    });
    

    これだけです。formに対してのイベントリスナーもいりません。

    dp.errorイベントを拾うことで、入力された値に不整合があると、値を空にすることが出来ます。

    フォーム送信時だけでなく、テキストフィールドからフォーカスが移ったときにも評価が走ります。

    ということで無事、datetimepickerへの入力制御を入れる事が出来ました!