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
valuedi un elemento<input>alla propmodelValue - Quando scatta un evento nativo
inputemettere un evento personalizzatoupdate:modelValuecon 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>