Vinculação Bidirecional com v-model em Componentes Vue.js e Modificadores Personalizados

Implementando v-model em Componentes Vue

Observação importante: O v-model funciona apenas com dados reativos do tipo ref. Não é possível utilizar diretamente com objetos reactive, pois a sincronização automática não ocorre nesse caso.

Abordagem 1: Vinculação Padrrão de um Campo

Criando o componente filho FormField.vue:

<template>
  <div class="campo">
    <input
      type="text"
      :value="modelValue"
      @input="dispararAtualizacao($event.target.value)"
    />
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const propriedades = defineProps({
  modelValue: {
    type: String,
    required: true
  }
})

const emitir = defineEmits(['update:modelValue'])

function dispararAtualizacao(valor) {
  emitir('update:modelValue', valor)
}
</script>

No componente pai, basta utilizar:

<template>
  <div>
    <FormField v-model="nomeCompleto" />
    <p>Valor atual: {{ nomeCompleto }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import FormField from './FormField.vue'

const nomeCompleto = ref('Digite aqui')
</script>

Aobrdagem 2: Múltiplas Vinculações com v-model Nomeado

Quando o componente precisa gerenciar mais de um campo reativo, utilizamos v-model:nomeDoCampo.

Componente PerfilForm.vue:

<template>
  <div>
    <input
      type="text"
      placeholder="Primeiro nome"
      :value="nome"
      @input="$emit('update:nome', $event.target.value)"
    />
    <input
      type="text"
      placeholder="Sobrenome"
      :value="sobrenome"
      @input="$emit('update:sobrenome', $event.target.value)"
    />
  </div>
</template>

<script setup>
defineProps({
  nome: String,
  sobrenome: String
})

defineEmits(['update:nome', 'update:sobrenome'])
</script>

Utilizando no componente pai:

<template>
  <div>
    <PerfilForm v-model:nome="primeiroNome" v-model:sobrenome="ultimoNome" />
    <p>Nome completo: {{ primeiroNome }} {{ ultimoNome }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import PerfilForm from './PerfilForm.vue'

const primeiroNome = ref('João')
const ultimoNome = ref('Silva')
</script>

Abordagem 3: Utilizando Propriedade Computada

Uma alternativa elegante é encapsular a lógica de leitura e escrita dentro de um computed com getter/setter.

Componente CampoControlado.vue:

<script setup>
import { computed, defineProps, defineEmits } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const conteudoLocal = computed({
  get: () => props.modelValue,
  set: (novoValor) => emit('update:modelValue', novoValor)
})
</script>

<template>
  <input v-model="conteudoLocal" />
</template>

Essa técnica centraliza toda a comunicação entre pai e filho de forma limpa e reativa.

Modificadores Personalizados para v-model

O Vue permite criar modificadores customizados que transformam o valor durante a sincronização. Por exemplo, vamos implementar um modificador caixaBaixa que converte automaticamente o texto para minúsculas.

Componente EntradaTexto.vue:

<template>
  <input
    type="text"
    :value="modelValue"
    @input="aoAlterar($event.target.value)"
  />
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: String,
  modelModifiers: {
    type: Object,
    default: () => ({})
  }
})

const emitir = defineEmits(['update:modelValue'])

function aoAlterar(valor) {
  if (props.modelModifiers.caixaBaixa) {
    valor = valor.toLowerCase()
  }
  emitir('update:modelValue', valor)
}
</script>

No componente pai, o modificador é aplicado diretamente na diretiva:

<template>
  <div>
    <EntradaTexto v-model.caixaBaixa="textoDigitado" />
    <p>Conteúdo: {{ textoDigitado }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import EntradaTexto from './EntradaTexto.vue'

const textoDigitado = ref('Exemplo de Texto')
</script>

Dessa forma, sempre que o usuário digitar algo, o valor será automaticamente convertido para letras minúsculas antes de ser atribuído à referência reativa.

Tags: vue3 v-model componentes props emits

Publicado em 6-23 06:28