v-model du composant
v-model
peut être utilisé sur un composant pour implémenter une liaison à double sens.
Tout d'abord, revoyons comment v-model est utilisé sur un élément natif :
template
<input v-model="searchText" />
Sous le capot, le compilateur de template transforme v-model en un équivalent plus verbeux pour nous. Ainsi, le code ci-dessus fait la même chose que ce qui suit :
template
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
Lorsqu'il est utilisé sur un composant, v-model
est alors équivalent à :
template
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
Toutefois, pour que cela fonctionne, le composant <CustomInput>
doit faire deux choses :
- Lier l'attribut value d'un élément natif
<input>
à la prop modelValue - Lorsqu'un évènement natif
input
est déclenché, émettre un évènement personnaliséupdate:modelValue
avec la nouvelle valeur
Voici cela en action :
vue
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
Maintenant v-model
devrait fonctionner parfaitement avec ce composant :
template
<CustomInput v-model="searchText" />
Une autre façon d'implémenter v-model
dans ce composant consiste à utiliser une propriété calculée
en écriture avec à la fois un accesseur et un mutateur. La méthode get
doit renvoyer la propriété modelValue
et la méthode set
doit émettre l'évènement correspondant :
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>
Les arguments de v-model
Par défaut, v-model
sur un composant utilise modelValue
comme prop et update:modelValue
comme évènement. Nous pouvons modifier ces noms en passant un argument à v-model
:
template
<MyComponent v-model:title="bookTitle" />
Dans ce cas, le composant enfant doit attendre une prop title
et émettre un évènement update:title
pour mettre à jour la valeur du composant parent :
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>
Liaisons multiple avec v-model
En tirant parti de la possibilité de cibler une prop et un évènement en particulier, comme nous l'avons appris précédemment avec les arguments de v-model
, nous pouvons désormais créer plusieurs liaisons v-model sur une seule instance de composant.
Chaque v-model se synchronisera avec une prop différente, sans avoir besoin d'options supplémentaires dans le composant :
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>
Gestion des modificateurs de v-model
Lorsque nous avons appris les liaisons d'entrée de formulaire, nous avons vu que v-model
avait des modificateurs natifs - .trim
, .number
et .lazy
. Dans certains cas, vous pouvez également souhaiter que le v-model
de votre composant d'entrée personnalisé prenne en charge les modificateurs personnalisés.
Créons un exemple de modificateur personnalisé, capitalize
, qui met en majuscule la première lettre de la chaîne de caractères fournie par la liaison v-model
:
template
<MyComponent v-model.capitalize="myText" />
Les modificateurs ajoutés à un v-model
de composant seront fournis au composant via la prop modelModifiers
. Dans l'exemple ci-dessous, nous avons créé un composant qui contient une prop modelModifiers
qui par défaut est un objet vide :
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>
Notez que la prop modelModifiers
du composant contient capitalize
et que sa valeur est true
- car elle est définie sur la liaison v-model
v-model.capitalize="myText"
.
Maintenant que notre prop est configurée, nous pouvons vérifier les clés de l'objet modelModifiers
et écrire un gestionnaire pour modifier la valeur émise. Dans le code ci-dessous, nous mettrons la chaîne de caractères en majuscule chaque fois que l'élément <input />
déclenche un évènement 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>
Pour les liaisons v-model
avec à la fois des arguments et des modificateurs, le nom de la prop générée sera arg + "Modifiers"
. Par exemple :
template
<MyComponent v-model:title.capitalize="myText">
Les déclarations correspondantes doivent être :
js
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }