🌊
defineModel()を理解する
今までの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.vue
とChildren.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