v-model з компонентами
v-model
можна використовувати в компоненті для реалізації двостороннього прив’язування.
Спочатку давайте переглянемо, як v-model
використовується на нативному елементі:
template
<input v-model="searchText" />
Під капотом компілятор шаблонів розширює v-model
до більш детального еквівалента для нас. Отже, наведений вище код робить те саме, що й наступний:
template
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
Коли використовується на компоненті, v-model
натомість розширюється до цього:
template
<CustomInput
:model-value="searchText"
@update:model-value="newValue => searchText = newValue"
/>
Щоб це справді спрацювало, компонент <CustomInput>
має виконати дві дії:
- Прив’язати атрибут
value
рідного елемента<input>
до реквізитуmodelValue
- Коли запускається нативна подія
input
, створити спеціальну подіюupdate:modelValue
з новим значенням
Ось це в дії:
vue
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
Тепер v-model
має ідеально працювати з цим компонентом:
template
<CustomInput v-model="searchText" />
Інший спосіб реалізації v-model
в цьому компоненті полягає у використанні властивості computed
, доступної для запису, як з геттером, так і з сеттером. Метод get
має повертати реквізит modelValue
, а метод set
має випромінювати відповідну подію:
vue
<!-- CustomInput.vue -->
<script setup>
import { computed } from 'vue'
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
</script>
<template>
<input v-model="value" />
</template>
Агрументи v-model
За промовчанням v-model
для компонента використовує modelValue
як реквізит і update:modelValue
як подію. Ми можемо змінити ці імена, передавши аргумент v-model
:
template
<MyComponent v-model:title="bookTitle" />
У цьому випадку дочірній компонент має очікувати реквізит title
і випромінювати подію update:title
для оновлення батьківського значення:
vue
<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
Кілька прив'язок v-model
Використовуючи можливість націлюватися на конкретний реквізит та подію, як ми дізналися раніше за допомогою аргументів v-model
, тепер ми можемо створити кілька прив’язок v-model
до одного екземпляра компонента.
Кожна v-model
синхронізуватиметься з іншими реквізитами без необхідності додаткових параметрів у компоненті:
template
<UserName
v-model:first-name="firstName"
v-model:last-name="lastName"
/>
vue
<script setup>
defineProps({
firstName: String,
lastName: String
})
defineEmits(['update:firstName', 'update:lastName'])
</script>
<template>
<input
type="text"
:value="firstName"
@input="$emit('update:firstName', $event.target.value)"
/>
<input
type="text"
:value="lastName"
@input="$emit('update:lastName', $event.target.value)"
/>
</template>
Обробка модифікаторів v-model
Коли ми вивчали прив’язки введення форм, ми побачили, що v-model
має вбудовані модифікатори – .trim
, .number
і .lazy
. У деяких випадках вам також може знадобитися, щоб v-model
у вашому власному компоненті введення підтримувала користувацькі модифікатори.
Давайте створимо приклад власного модифікатора, capitalize
, який робить першу літеру рядка великою, що надається прив’язкою v-model
:
template
<MyComponent v-model.capitalize="myText" />
Модифікатори, додані до v-model
, будуть надані компоненту через реквізит modelModifiers
. У наведеному нижче прикладі ми створили компонент, який містить реквізит modelModifiers
, яка за промовчанням має порожній об’єкт:
vue
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
defineEmits(['update:modelValue'])
console.log(props.modelModifiers) // { capitalize: true }
</script>
<template>
<input
type="text"
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
Зауважте, що реквізит modelModifiers
компонента містить capitalize
, а його значення — true
, оскільки воно прив’язано до v-model
v-model.capitalize="myText"
.
Тепер, коли ми налаштували реквізит, ми можемо перевірити ключі об’єкта modelModifiers
і написати обробник для зміни випромінюваного значення. У наведеному нижче коді ми будемо використовувати рядок з великої літери щоразу, коли елемент <input />
запускає подію input
.
vue
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
Модифікатори для v-model
з аргументами
Для прив’язок v-model
як з аргументом, так і з модифікаторами, ім’я створеного реквізита буде arg + "Modifiers"
. Наприклад:
template
<MyComponent v-model:title.capitalize="myText">
Відповідні оголошення мають бути:
js
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
Ось ще один приклад використання модифікаторів із кількома v-model
з різними аргументами:
template
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
vue
<script setup>
const props = defineProps({
firstName: String,
lastName: String,
firstNameModifiers: { default: () => ({}) },
lastNameModifiers: { default: () => ({}) }
})
defineEmits(['update:firstName', 'update:lastName'])
console.log(props.firstNameModifiers) // { capitalize: true }
console.log(props.lastNameModifiers) // { uppercase: true}
</script>