v-date-pickerでinput+カレンダーアイコンを使う
仕事でv-date-pickerを使う機会があったので、苦戦したことなどを備忘録としてまとめます。
v-date-pickerとは?
vue.jsのためのカレンダープラグインです。 インストール方法などはこちらをどうぞ。
今回やりたかったこと
カレンダーを表示させるトリガーとして、inputを使用します。 inputにはカレンダーアイコンを付けて、ユーザに「ここはカレンダーから入力するんだな」と認識してもらいます。
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 (日本標準時)
みたいなやつ)が入ってしまいます。
そこで、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日") } } });