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
inputest déclenché, émettre un événement personnaliséupdate:modelValueavec 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>Modificateurs pour v-model avec arguments
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 }Voici un autre exemple d'utilisation de modificateurs avec plusieurs v-model ayant des arguments différents :
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>