Il v-model sui componenti
v-model
può essere utilizzato su un componente per implementare un legame bidirezionale.
Prima di tutto, rivediamo come viene utilizzato v-model
su un elemento nativo:
template
<input v-model="searchText" />
Nel backend, il compilatore del template espande v-model
in un modo più verboso. Quindi, il codice sopra fa la stessa cosa del seguente:
template
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
Quando usato su un componente, v-model
invece si espande in questo modo:
template
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
Affinché ciò funzioni effettivamente, il componente <CustomInput>
deve fare due cose:
- Collegare l'attributo
value
di un elemento<input>
alla propmodelValue
- Quando scatta un evento nativo
input
emettere un evento personalizzatoupdate:modelValue
con il nuovo valore
Una dimostrazione pratica:
vue
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
Ora il v-model
dovrebbe funzionare perfettamente con questo componente:
template
<CustomInput v-model="searchText" />
Un altro modo per implementare v-model
all'interno di questo componente è utilizzare una proprietà computed
scrivibile con sia un getter che un setter. Il metodo get
dovrebbe restituire la proprietà modelValue
e il metodo set
dovrebbe emettere l'evento corrispondente:
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>
Argomenti v-model
Per impostazione predefinita, v-model
su un componente utilizza modelValue
come prop e update:modelValue
come evento. Possiamo modificare questi nomi passando un argomento a v-model
:
template
<MyComponent v-model:title="bookTitle" />
In questo caso, il componente figlio dovrebbe aspettarsi una prop title
ed emettere un evento update:title
per aggiornare il valore nel componente genitore:
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>
Molteplici v-model
Sfruttando la capacità di specificare una prop e un evento particolari come abbiamo imparato precedentemente con gli argomenti del v-model
, ora possiamo creare più collegamenti v-model
su una singola istanza del componente.
Ogni v-model
si sincronizzerà con una prop diversa, senza bisogno di opzioni aggiuntive nel componente:
template
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
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>
Gestire i modificatori del v-model
Quando abbiamo imparato a proposito dei vincoli per gli input nei form, abbiamo visto che v-model
ha modificatori integrati - .trim
, .number
e .lazy
. In alcuni casi, potresti anche voler supportare modificatori personalizzati per il v-model
nel tuo componente input personalizzato.
Creiamo un esempio di modificatore personalizzato chiamato capitalize
, che rende maiuscola la prima lettera della stringa fornita dal binding v-model
:
template
<MyComponent v-model.capitalize="myText" />
I modificatori aggiunti a un componente v-model
saranno forniti al componente tramite la prop modelModifiers
. Nell'esempio qui sotto, abbiamo creato un componente che contiene una prop modelModifiers
che per impostazione predefinita è un oggetto vuoto:
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>
Nota che la prop modelModifiers
del componente contiene capitalize
e il suo valore è true
- perché è stato impostato sul binding v-model
v-model.capitalize="myText"
.
Ora che abbiamo la nostra prop impostata, possiamo controllare le chiavi dell'oggetto modelModifiers
e scrivere un gestore per cambiare il valore emesso. Nel codice qui sotto, rendiamo maiuscola la stringa ogni volta che l'elemento <input />
scatena un evento 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>
Modificatori per il v-model
con argomenti
Per i binding v-model
con sia un argomento che dei modificatori, il nome della prop generata sarà arg + "Modifiers"
. Ad esempio:
template
<MyComponent v-model:title.capitalize="myText">
Le dichiarazioni corrispondenti dovrebbero essere:
js
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }
Ecco un altro esempio di utilizzo dei modificatori con più v-model
con argomenti diversi:
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>