Binding přes v-model
Základní použití
Direkrivu v-model
lze použít pro implementaci obousměrného (two-way) bindingu.
Od verze Vue 3.4 je doporučený postup použití makra defineModel()
:
vue
<!-- Child.vue -->
<script setup>
const model = defineModel()
function update() {
model.value++
}
</script>
<template>
<div>hodnota v-model z rodiče je: {{ model }}</div>
</template>
Komponenta rodiče pak může provést binding hodnoty pomocí v-model
:
template
<!-- Parent.vue -->
<Child v-model="count" />
Hodnota vrácená z defineModel()
je ref. Může být přistupována a měněna jako jakýkoliv jiný ref, kromě toho, že slouží jako obousměrný binding mezi hodnotou z rodiče a tou lokální:
- Jeho
.value
je synchronizována s hodnotou navázanou pomocív-model
v komponentě rodiče; - Když je změněna komponentou potomka, způsobí automaticky aktualizaci hodnoty i v komponentě rodiče.
To znamená, že můžete také provést binding tohoto refu na nativní input element pomocí v-model
, čímž se dá vstupní pole jednoduše a přímočaře obalit:
vue
<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template>
Pod pokličkou
defineModel
je makro pro usnadnění zápisu. Překladač jej rozšíří na:
- vlastnost (prop)
modelValue
, se kterou je synchronizována lokální hodnota ref; - událost (event)
update:modelValue
, která je emitována při změně lokální hodnoty ref.
Takto se implementovala komponenta potomka se stejnou funkcionalitou před verzí 3.4:
vue
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
Jak můžete vidět, je to trochu zbytečně složité. Nicméně je dobré vědět, jak to ve skutečnosti funguje.
Protože defineModel
deklaruje vlastnost (prop), mužete následně upravovat její možnosti (options) předáním do defineModel
:
js
// v-model bude povinná hodnota
const model = defineModel({ required: true })
// poskytnutí výchozí hodnoty
const model = defineModel({ default: 0 })
v-model
parametry
v-model
na komponentě může také přijmout parametr:
template
<MyComponent v-model:title="bookTitle" />
V komponentě potomka můžeme takový parametr podporovat předáním řetězce do defineModel()
vue
<!-- MyComponent.vue -->
<script setup>
const title = defineModel('title')
</script>
<template>
<input type="text" v-model="title" />
</template>
Pokud je potřeba předat i možnosti (options) vlastnosti (prop), je mpžné je předat jako druhý parametr po názvu:
js
const title = defineModel('title', { required: true })
Použití před Vue 3.4
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ícenásobný binding přes v-model
yužitím možnosti zaměřit se na konkrétní vlastnost a událost, jak jsme se to naučili dříve pomocí v-model
parametrů, můžeme nyní na jedné instanci komponenty vytvořit více v-model
vazeb.
Každý v-model
se bude synchronizovat s jinou vlastností bez nutnosti speciální konfigurace uvnitř komponenty:
template
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
vue
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
Pre 3.4 Usage
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>
Obsluha v-model
modifikátorů
Když jsme se učili o bindingu dat z formuláře, viděli jsme, že v-model
má vestavěné modifikátory - .trim
, .number
a .lazy
. V některých případech můžete chtít, aby v-model
na vaší vlastní input komponentě podporoval vlastní modifikátory také.
Pojďme vytvořit příklad vlastního modifikátoru capitalize
, který bude psát první znak řetězce zadaného přes v-model
binding velkými písmeny:
template
<MyComponent v-model.capitalize="myText" />
Modifikátory přidané na v-model
komponenty mohou být získány v komponentně potomka pomocí dekonstrukce návratové hodnoty defineModel()
takto:
vue
<script setup>
const [model, modifiers] = defineModel()
console.log(modifiers) // { capitalize: true }
</script>
<template>
<input type="text" v-model="model" />
</template>
Pro podmíněné nastavení jak by měla být hodnota na základě modifikátorů čtena / zapisována, můžeme do defineModel()
předat možnosti get
a set
. Tyto dvě nastavení získají hodnotu při čtení / zápisu příslušné ref hodnoty a měly by vrátit transformovanou hodnotu. Takto můžeme použít set
pro implementaci modifikátoru capitalize
:
vue
<script setup>
const [model, modifiers] = defineModel({
set(value) {
if (modifiers.capitalize) {
return value.charAt(0).toUpperCase() + value.slice(1)
}
return value
}
})
</script>
<template>
<input type="text" v-model="model" />
</template>
Použití před verzí 3.4
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>
Modifikátory pro v-model
s parametry
Here's another example of using modifiers with multiple v-model
with different arguments:
template
<UserName
v-model:first-name.capitalize="first"
v-model:last-name.uppercase="last"
/>
vue
<script setup>
const [firstName, firstNameModifiers] = defineModel('firstName')
const [lastName, lastNameModifiers] = defineModel('lastName')
console.log(firstNameModifiers) // { capitalize: true }
console.log(lastNameModifiers) // { uppercase: true}
</script>
Pre 3.4 Usage
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>