しゃけlog

思考の整理めも

v-date-pickerでinput+カレンダーアイコンを使う

仕事でv-date-pickerを使う機会があったので、苦戦したことなどを備忘録としてまとめます。

v-date-pickerとは?

vue.jsのためのカレンダープラグインです。 インストール方法などはこちらをどうぞ。

今回やりたかったこと

カレンダーを表示させるトリガーとして、inputを使用します。 inputにはカレンダーアイコンを付けて、ユーザに「ここはカレンダーから入力するんだな」と認識してもらいます。

f:id:mgmg_shake:20200805230825g:plain
v-date-pickerの挙動イメージ

inputをタップするとカレンダーが開く仕組みです。

つまづいた事

カレンダーアイコンを表示するためには、v-date-pickerによって生成されるhtmlにCSSを適応する必要がありました。 しかしinputには擬似要素をつけることはできません。 そこで、v-date-pickerをdivで囲み、そのdivに擬似要素としてアイコンをつける方式をとりました。

こんな感じです。

<div class="datepicker1">
    <v-date-picker :mode="'single'"
                   :formats="formats"
                   :popover="{ visibility: 'click' }"
                   class="datepicker__input"
                   v-model="date1">
    </v-date-picker>
  </div>
  .datepicker1 {
    position: relative;
      &::after {
        content: "";
        display: block;
        position: absolute;
        width: 28px;
        height: 28px;
        background: url(path/to/icon) center / 100% no-repeat;
        top: 10px;
        right: 16px;
      }
}

これで「見かけ上は」カレンダーアイコンを設置することに成功しました。 しかし、カレンダーアイコンはv-date-pickerの親のクラスに付与されたものなので、クリックが効きませんでした。

v-date-pickerによって生成されたinputをクリックするとカレンダーが開きます。しかし、カレンダーアイコンがよそから上に乗っかってきている形になるので、カレンダーアイコンをクリックしてもカレンダーは開かないというユーザビリティが残念な挙動になってしまいました。

解決策

v-date-pickerの中に手動でinputを入れることができました。

  <div class="datepicker2">
     <v-date-picker  :mode="'single'"
                    :formats="formats"
                    :popover="{ visibility: 'click' }"
                    v-model="date2">
      <div class="datepicker__input">
        <input type="input" :value="date2">
        <span class="input-group__date_icon"></span>
      </div>
    </v-date-picker>
  </div>

枠を表示するinputと、アイコンを表示するspanの2つの要素を中に入れたかったのですが、これらを並べるだけだと表示されませんでした。 これらの2つの要素を、divで囲んでひとかたまりにしてあげることが表示されました。コンポーネント的な考えなのだと思います。(多分

ただしこの方法では、v-date-pickerにpropしていたformatsが反映されないため、inputに生の日時情報(Mon Aug 31 2020 00:00:00 GMT+0900 (日本標準時)みたいなやつ)が入ってしまいます。

f:id:mgmg_shake:20200805230906p:plain
日付が読みにくいinput

そこで、v-date-pickerのv-modelにはサーバサイドに渡す形式の変数を置いておき、inputにはその変数をmoment.jsか何かで読みやすくした形式にしてv-on:valueする一手間が必要になります。

例では、サーバサイドに渡す日付をdate2としています。 input内に表示する日付は、computedを用いてdate2をmoment.jsでトリミングしたtrimDate2を使用しています。 これで、見かけ上は読みやすい日時ですが、サーバーサイドに渡す日付はきちんとした形になります。

  <div class="datepicker2">
     <v-date-picker  :mode="'single'"
                    :formats="formats"
                    :popover="{ visibility: 'click' }"
                    v-model="date2">
      <div class="datepicker__input">
        <input type="input" :value="trimDate2">
        <span class="input-group__date_icon"></span>
      </div>
    </v-date-picker>
  </div>
new Vue({
  el: '#app',
  data: {
    date1: "",
    date2: ""
  },
  computed: {
    trimDate2() {
      if(this.date2 === "")
        return ""

      return moment(this.date2).format("YYYY年MM月DD日")
    }
  }
});

参考文献

ブログ執筆にあたって参考にした記事