thumbnail

【Vue.js3】カスタムコンポーネントでv-modelを使う方法

Vue3のカスタムコンポーネントにおけるv-modelの使用方法について解説します。また、Vue2ユーザのためにVue3での仕様変更についても併せて説明しています。Vue3での仕様変更によって、Vue2で動いていたコードが動かなくなります。例えばpropsのvalueがmodelValueに変わっていたり、emitの発火にupdateを記述するなど変更点が多いので、順を追って解説していきます。

Vue3での変更点一覧

この章はVue2ユーザのみお読み下さい。
Vue3で始めてカスタムコンポーネントのv-modelを扱う方は、次の章へお進み下さい。

Vue3における変更点は次のとおりです。

  • 子コンポーネントに渡るデフォルトのprops名はmodelValueに変更
  • 子コンポーネントからemitする際のイベント名の変更、決まり
  • modelオプションの削除
  • 複数バインディングにv-bind.syncは使わない

以下の章でそれぞれの変更点についても解説します。

カスタムコンポーネントにおけるv-modelの使用方法

Vue3での記述方法

カスタムコンポーネントにおけるv-modelは、modelValueプロパティを渡してupdate:modelValueイベントを発火させるのと等価です。

  <Child v-model="text"/>
  <!-- 下と同じ意味 -->
  <Child :modelValue="text" @update:modelValue="text = $event"/>

よって、子コンポーネント(カスタムコンポーネント)側ではmodelValueプロパティを受け取る準備と、update:modelValueイベントをemitさせる処理を記述すれば良いことになります。

したがって、子カスタムコンポーネント側のコードは次のようになります。
(以下では簡単な例として、カスタムコンポーネントのinputフォームのinputイベントを発火させています。)

・テンプレート

<template>
  <input :value="modelValue" type="text" @input="onInputText" />
</template>

・スクリプト

<script lang="ts" setup>
const props = defineProps<{modelValue: string}>()
const emits = defineEmits<{(e: 'update:modelValue', text: string): void}>()

const onInputText = (e: Event) => {
  const target = e.target as HTMLInputElement
  emits('update:modelValue', target.value)
}
</script>

※注意
この記事では上のコードのようにscript setup構文で記述しています。script setup構文について詳しく知りたい方は次の記事を参照下さい。

Vue2からの変更点

①propsのデフォルト名はmodelValue

Vue2はデフォルトでvalueという名前のpropsが子コンポーネントに渡っていましたが、Vue3ではmodelValueという名前に変更されています。

したがって、子コンポーネントではmodelValueというpropsを受け取る記述をします。

<script lang="ts" setup>
const props = defineProps<{modelValue: string}>()
</script>

②inputをemitできない

Vue2の子コンポーネントで$emit('input', event.target.value)などとしてイベントを送信していましたが、Vue3でこのコードは動かなくなります。

代わりにemit('update:modelValue', event.target.value)とします。

プロパティ名をmodelValueから変更する方法

Vue3での記述方法

デフォルトではmodelValueがプロパティに渡っていましたが、これは変更することが出来ます。
そのためにはv-modelの引数を使います。

その記述方法ですが、v-model:プロパティ名=値のようにv-modelにコロンをつけて引数名を続けて書きます。

それに伴い、発火させるemitのイベント名もupdate:プロパティ名のように変更します。

以上を踏まえて、textという名前のプロパティ名に変更する例を示します。
先程の例と同じではつまらないので、今回はchangeイベントを発生させています。

・親コンポーネント

<template>
   <MyComponent v-model:text="data"/>
</template>

・子コンポーネントのテンプレート

<template>
  <input :value="text" type="text" @change="onChangeText" />
</template>

・子コンポーネントのスクリプト

<script lang="ts" setup>
const props = defineProps<{text: string}>()
const emits = defineEmits<{(e: 'update:text', text: string): void}>()

const onChangeText = (e: Event) => {
  const target = e.target as HTMLInputElement
  emits('update:text', target.value)
}
</script>

Vue2からの変更点

プロパティ名はv-modelの引数で定義

Vue2では子コンポーネントにmodelオプションを用意し、紐づくプロパティやイベントを変更していました。しかしVue3ではこのオプションは削除されました。代わりにv-modelの引数を用いることになります。

1つのコンポーネントに複数のv-modelを使う方法

Vue3での記述方法

前節で出てきた引数を使い分ければ、同時に複数のバインディングが可能になります。

次の例では、コンポーネントに2つv-modelを付けています。

・親コンポーネント

<template>
  <Child 
    v-model:text="data1" 
    v-model="data2"
  />
</template>

子コンポーネントのテンプレート

<template>
  <input type="text" :value="text" @input="onInputText">
  <input type="number" :value="modelValue" @change="onChangeNumber">
</template>

子コンポーネントのスクリプト

<script lang="ts" setup>
const props = defineProps<{
  text: string
  modelValue: number
  }>()

const emits = defineEmits<{
  (e: 'update:text', text: string): void
  (e: 'update:modelValue', num: number): void
  }>()

const onInputText = (e: Event) => {
  const target = e.target as HTMLInputElement
  emits('update:text', target.value)
}

const onChangeNumber = (e: Event) => {
  const target = e.target as HTMLInputElement
  emits('update:modelValue', Number(target.value))
}
</script>

Vue2からの変更点

Vue2では、.syncを使って記述していました。代わりにv-modelの引数を利用します。

まとめ

以上、Vue3でカスタムコンポーネントのv-modelの使用方法について、その使用方法と変更点を説明しました。これさえ知っておけば最低限のコードは記述できるはずです。

参考

Vue.js公式ドキュメント

https://v3.ja.vuejs.org/guide/migration/v-model.html#%E6%A6%82%E8%A6%81