0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vuetifyのv-time-pickerをループ内に実装する

Posted at

#やりたいこと
Vuetifyのtimepickerを使用し、テキストボックスに選択した時分を入れたい。 ↓こんなやつ。
image.png

これを、画面上の複数の時刻入力すべてに対応させたい。

まずやったこと

入力が1つだけの基本的なやり方はVuetifyの公式サイトを参照。
https://vuetifyjs.com/ja/components/time-pickers/#section-30c030a430a230ed30b0306830e130cb30e530fc

本家のサンプルを基に自分なりに少々カスタマイズ。

(サンプルベース)page.vue
<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つのテキストボックスが表示される。
image.png

テキストボックスをクリックすると、3つ分のtimepickerが表示されてしまう:no_good:
image.png

timepickerが1つのサンプルをv-forの中に入れただけなので当然です。
内容を配列に対応させる必要があります。

どこに手を入れるか

さあ、そもそもこの公式サンプルが何をやっているのか、正直説明が少なくてよくわかりません。

見てみると、使用されている変数に以下3つがあります。

  • time
  • menu
  • menu2

これらがどう影響するのか...
一応、自分なりの解釈は以下の通りです。

  • time:入力した時分の値
  • menu:timepickerのポップアップ表示状態
  • menu2:timepickerは「時」→「分」と2段階の選択が必要で、ポップアップを開き直したときに必ず「時」から開くために設定されているもの(のようです)

とりあえず以下のように配列対応したら想定通りの動作をしました。

(対応後)page.vue
<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)

image.png
お見事:clap_tone1:

やったこと

基本的には以下の対応をしています。

  • 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(略) のように記述してしまうと、「分」を選択時に以下のエラーがコンソールに出力され、正常に動作しません:imp:

TypeError: _vm.$refs[("menu_" + idx)].save is not a function

所感

複数の timepicker に対応することができました。
しかし、上記でのエラーに遭遇してからすぐに解決できず、ずっと「???」状態でした。
v-for内のrefが配列にされるのはvueでは常識なのでしょうか?
そして、上記のエラーはなぜ発生するのでしょうか?
どこか公式の記述をご存知の方がいらしたら、ぜひお教えください。

ありがとうございました:angel:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?