O Modelo Virtual do Componente
A v-model
pode ser usada sobre um componente para implementar uma vínculo de duas vias.
Primeiro vamos revisitar como a v-model
é usada sobre um elemento nativo:
template
<input v-model="searchText" />
Nos bastidores, o compilador do modelo de marcação expande a v-model
para o equivalente mais verboso por nós. Então o código acima faz o mesmo que o seguinte:
template
<input
:value="searchText"
@input="searchText = $event.target.value"
/>
Quando usado sobre um componente, a v-model
expande para isto:
template
<CustomInput
:modelValue="searchText"
@update:modelValue="newValue => searchText = newValue"
/>
Mas para isto realmente funcionar, o componente <CustomInput>
deve fazer duas coisas:
- Vincular o atributo
value
de um elemento<input>
nativo à propriedademodelValue
- Quando um evento
input
nativo é acionado, emite um eventoupdate:modelValue
personalizado com o novo valor
Cá está aquilo em ação:
vue
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
Agora a v-model
deve funcionar perfeitamente com este componente:
template
<CustomInput v-model="searchText" />
Um outra maneira de implementar a v-model
dentro deste componente é usar uma propriedade computed
graváveis com ambos um recuperador e um definidor. O método get
deve retornar a propriedade modelValue
e o método set
deve emitir o evento correspondente:
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>
Argumentos de v-model
Por padrão, a v-model
em um componente usa modelValue
como a propriedade e o update:modelValue
como o evento. Nós podemos modificar estes nomes passando um argumento para v-model
:
template
<MyComponent v-model:title="bookTitle" />
Neste caso, o componente filho deve esperar uma propriedade title
e emitir um evento update:title
para atualizar o valor do componente pai:
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árias Vinculações de v-model
Ao influenciar a habilidade de escolher uma propriedade e evento em particular como alvo conforme aprendemos antes com os argumentos de v-model
, podemos agora criar várias vinculações de v-model
sobre uma única instância de componente.
Cada v-model
se sincronizará à uma propriedade diferente, sem a necessidade para opções adicionais em um 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>
Manipulando Modificadores de v-model
Quando estávamos a aprender a respeito das vinculações de entrada do formulário, vimos que a v-model
tem modificadores embutidos - .trim
, .number
, e .lazy
. Em alguns casos, também podes querer que a v-model
no teu componente de entrada personalizado suportar modificadores personalizados.
Vamos criar um exemplo de modificador personalizado, capitalize
, que escreve com maiúsculas a primeira letra da sequência de caracteres fornecidos pelo vinculo da v-model
:
template
<MyComponent v-model.capitalize="myText" />
Os modificadores adicionados para uma v-model
do componente serão fornecidos ao componente através da propriedade modelModifiers
. No exemplo abaixo, criamos um componente que contém de uma propriedade modelModifiers
que predefine para um objeto vazio:
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>
Repara que a propriedade modelModifiers
do componente contém capitalize
e seu valor é true
- devido a ela estar definida no v-model.capitalize="myText"
do vinculo da v-model
.
Agora que temos a nossa propriedade definida, podemos verificar as chaves do objeto modelModifiers
e escrever um manipulador para mudar o valor emitido. No código abaixo escreveremos com maiúsculas a primeira letra da sequência de caracteres sempre que o elemento <input />
disparar um evento de 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>
Para os vínculos de v-model
com ambos argumento e modificadores, o nome da propriedade gerada será arg + "Modifiers"
. Por exemplo:
template
<MyComponent v-model:title.capitalize="myText">
As declarações correspondente devem ser:
js
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }