【Vuetify】v-selectで@changeイベントは使用不可

Vuetifyのv-selectコンポーネントを使っていましたが、@changeイベントが発火されずハマったので備忘録としてまとめます。

バージョン
  • Vue.js 3.3.4
  • Vuetify 3.4.4
目次

問題

私の職場では「Vue.jsのemitは使わずpropsに更新用の関数を渡す」方針となっています。

具体的には以下のSampleContainer.vueのように、selectedAnimalIdを更新するためのupdateSelectedAnimalId関数も子コンポーネントに渡しています。

<script setup lang="ts">
import { reactive, ref } from 'vue';
import SamplePresentation from '../presentation/SamplePresentation.vue';

const animals = reactive([
  { id: 1, name: '犬' },
  { id: 2, name: '猫' },
  { id: 3, name: 'うさぎ'},
  { id: 4, name: 'ハムスター' },
  { id: 5, name: 'モルモット' },
])

// 選択された animals のID
const selectedAnimalId = ref()
// selectedAnimalId を更新するための関数
const updateSelectedAnimalId = (newAnimalId: number) => {
  selectedAnimalId.value = newAnimalId
}
</script>

<template>
  <v-container>
    <SamplePresentation
      :animals="animals"
      :selected-animal-id="selectedAnimalId"
      :update-selected-animal-id="updateSelectedAnimalId"
    />
  </v-container>
</template>

子コンポーネントのSamplePresentation.vue側で、v-select@changeイベントを設けました。

これにより選択肢を変更するタイミングでupdateSelectedAnimalIdが実行され、親コンポーネントのselectedAnimalIdを更新できると考えましたが、うまくいきませんでした、、。

<script setup lang="ts">
export type Props = {
  animals: { id: number, name: string }[]
  selectedAnimalId: number
  updateSelectedAnimalId: (newAnimalId: number) => void
};

defineProps<Props>()
</script>

<template>
  <v-container>
    <v-select
      label="好きな動物"
      :items="animals.map((animal) => animal.name)"
      item-title="name"
      item-value="id"
      @change="updateSelectedAnimal($event.target.value)"
    />
  </v-container>
</template>

解決方法

Vuetifyのv-selectコンポーネントはそもそも@changeイベントを受け付けていないことが原因でした。

代わりにv-modelを使う必要があるようです。

そこで上記のSamplePresentation.vueを次のように修正しました。

<script setup lang="ts">
import { ref } from 'vue';
import { watch } from 'vue';

export type Props = {
  animals: { id: number, name: string }[]
  selectedAnimalId: number
  updateSelectedAnimalId: (newAnimalId: number) => void
};

const props = defineProps<Props>()

// 以下を追加
const localSelectedAnimalId = ref();
watch(localSelectedAnimalId, () => {
  props.updateSelectedAnimalId(localSelectedAnimalId.value);
});
</script>

<template>
  <v-container>
    <v-select
      label="好きな動物"
      :items="animals"
      item-title="name"
      item-value="id"
      v-model="localSelectedAnimalId"
    />
  </v-container>
</template>

Vue.jsのpropsはコンポーネント側で直接変更できません。

なので親コンポーネントから渡されたselectedAnimalIdを使って直接v-model="selectedAnimalId"と書くとエラーになってしまいます。

そこで以下の実装を施しました。

  1. SamplePresentation.vue内にlocalSelectedAnimalIdを設ける
  2. v-model="localSelectedAnimalId"とすることでv-selectのvalueとlocalSelectedAnimalIdを連動させる
  3. localSelectedAnimalIdが変更されるたびにwatchプロパティを用いてprops.updateSelectedAnimalIdを実行する
  4. 親コンポーネントのselectedAnimalIdを更新できるようになった!

おわりに

VuetifyのようなUIコンポーネントライブラリを使う際は、そのコンポーネントがどのpropsやeventを受け付けているのか、ドキュメントを見てしっかり確認する必要があると学びました。

参考文献

技術書が好きなエンジニア向け

あわせて読みたい
【エンジニア向け】効率的な技術書の読み方5選【経験談】 スキルアップに直結させるための効率的な技術書の読み方を知りたくないですか?この記事ではエンジニア向けに効果的なインプットのための技術書の読み方を5つ紹介します。最後まで読めば自分に合う読み方が1つくらい見つかるはずです。ぜひご一読ください。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次