🌊

defineModel()を理解する

2024/04/14に公開

今までのv-model

フォームでユーザーが入力した値をバインド(jsの値と紐づける)するには下記のようにイベントの発火に伴い値をバインドする方法があります。

1️⃣ v-modelを使わない場合
<template>
  <div>
    <form>
      <input
        type="text"
        :value="inputName"
        @input="(e) => inputName = e.target.value"
        name="name"
      />
    </form>
    <p>Name: {{ inputName }}</p>
  </div>
</template>

<script setup lang="ts">
const inputName = ref('')
</script>
挙動

v-modelは上記の書き方をシンプルにできます。(挙動は同じです)

2️⃣ v-modelの場合
<template>
  <div>
    <form>
      <input
        type="text"
        v-model="inputName"
        name="name"
      />
    </form>
    <p>Name: {{ inputName }}</p>
  </div>
</template>

<script setup lang="ts">
const inputName = ref('')
</script>

v-modelを使うことでシンプルに書けることが分かりました。
ただ今はまだ一つのコンポーネント内で使用している単純な場合です。実際にはInputはatomsのように分割して色々な画面で使い回すことが多いと思います。

コンポーネント間での受け渡し

Parent.vueChildren.vueに分割した場合を見ていきます。

まず、親コンポーネントでは子コンポーネントのChildrenを読み込み、inputNameを渡します。

Parent.vue
<template>
  <div>
    <form>
      <Children v-model="inputName" />
    </form>
    <p>Name: {{ inputName }}</p>
  </div>
</template>

<script setup lang="ts">
import Children from '../atoms/Children.vue'
const inputName = ref('')
</script>

子コンポーネントでは2️⃣のコードのinputタグをコンポーネントとして切り出します。

Children.vue
<template>
  <input
    type="text"
    name="name"
    :value="props.modelValue"
    @input="emits('update:modelValue', $event.target?.value)"
  />
</template>

<script setup lang="ts">
type Props = {
  modelValue: string;
}
type Emits = {
  (eventName: 'update:modelValue', value: string): void
};

const props = defineProps<Props>();
const emits = defineEmits<Emits>();
</script>

コンポーネントを分割することはできましたが、defineModel()を使用することでとっても簡潔にまとめられます。

defineModel()を使ってみる

Children.vue(defineModel())
<template>
  <input
    type="text"
    name="name"
    v-model="inputValue"
  />
</template>

<script setup lang="ts">
const inputValue = defineModel();
</script>

複数のv-modelをバインドする

v-modelには引数を指定することができます。
<Children v-model:input="inputName" />
この場合、子コンポーネントでは下記のようにdefineModelの第一引数に文字列を渡すことで対応する引数を紐づけられます。
const modelValue = defineModel('input');

つまり一つのコンポーネントに複数のv-modelを作成することが可能になります。

Discussion