#やりたいこと
Vuetifyのtimepickerを使用し、テキストボックスに選択した時分を入れたい。 ↓こんなやつ。
これを、画面上の複数の時刻入力すべてに対応させたい。
まずやったこと
入力が1つだけの基本的なやり方はVuetifyの公式サイトを参照。
https://vuetifyjs.com/ja/components/time-pickers/#section-30c030a430a230ed30b0306830e130cb30e530fc
本家のサンプルを基に自分なりに少々カスタマイズ。
<template>
<v-container style="width:320px;">
<v-card v-for="item in items" :key="item.id">
<v-menu
ref="menu"
v-model="menu2"
:close-on-content-click="false"
:return-value.sync="time"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="time"
label="Picker in menu"
prepend-icon="mdi-clock-time-four-outline"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-time-picker
v-if="menu2"
v-model="time"
full-width
format="24hr"
@click:minute="$refs.menu.save(time)"
></v-time-picker>
</v-menu>
</v-card>
</v-container>
</template>
<script>
export default {
name: 'IndexPage',
data() {
return {
menu: false,
menu2:false,
time: '',
items: [
{ id: 1, name: 'abc' },
{ id: 2, name: 'def' },
{ id: 3, name: 'ghi' },
],
}
},
}
</script>
結果(NG)
データを3つ用意しているので3つのテキストボックスが表示される。
テキストボックスをクリックすると、3つ分のtimepickerが表示されてしまう
timepickerが1つのサンプルをv-forの中に入れただけなので当然です。
内容を配列に対応させる必要があります。
どこに手を入れるか
さあ、そもそもこの公式サンプルが何をやっているのか、正直説明が少なくてよくわかりません。
見てみると、使用されている変数に以下3つがあります。
- time
- menu
- menu2
これらがどう影響するのか...
一応、自分なりの解釈は以下の通りです。
- time:入力した時分の値
- menu:timepickerのポップアップ表示状態
- menu2:timepickerは「時」→「分」と2段階の選択が必要で、ポップアップを開き直したときに必ず「時」から開くために設定されているもの(のようです)
とりあえず以下のように配列対応したら想定通りの動作をしました。
<template>
<v-container style="width:320px;">
<v-card v-for="(item, idx) in items" :key="item.id">
<v-menu
ref="menu"
v-model="menu2[item.id]"
:close-on-content-click="false"
:return-value.sync="time[item.id]"
transition="scale-transition"
offset-y
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
v-model="time[item.id]"
label="Picker in menu"
prepend-icon="mdi-clock-time-four-outline"
readonly
v-bind="attrs"
v-on="on"
></v-text-field>
</template>
<v-time-picker
v-if="menu2[item.id]"
v-model="time[item.id]"
full-width
format="24hr"
@click:minute="$refs.menu[idx].save(time[item.id])"
></v-time-picker>
</v-menu>
</v-card>
</v-container>
</template>
<script>
export default {
name: 'IndexPage',
data() {
return {
menu: {},
menu2:{},
time: [],
items: [
{ id: 1, name: 'abc' },
{ id: 2, name: 'def' },
{ id: 3, name: 'ghi' },
],
}
},
}
</script>
結果(OK)
やったこと
基本的には以下の対応をしています。
- time → time[idx]
- menu2 → menu2[idx]
しかし!v-menu内の ref="menu" はそのままです。
そして v-time-picker 内で参照していますが、その記述は $refs.menu[idx] です。
これは、v-for内のrefは勝手に配列にされるためです。
refの記述も配列に対応する必要があると考え、
ref="menu"
と $refs.menu[idx].save(略)
の組み合わせを、
:ref="`menu_${idx}`"
と $refs[`menu_${idx}`].save(略)
のように記述してしまうと、「分」を選択時に以下のエラーがコンソールに出力され、正常に動作しません
TypeError: _vm.$refs[("menu_" + idx)].save is not a function
所感
複数の timepicker に対応することができました。
しかし、上記でのエラーに遭遇してからすぐに解決できず、ずっと「???」状態でした。
v-for内のrefが配列にされるのはvueでは常識なのでしょうか?
そして、上記のエラーはなぜ発生するのでしょうか?
どこか公式の記述をご存知の方がいらしたら、ぜひお教えください。
ありがとうございました