(Vue3) Link form component object with parent object using v-model and Options API?

24 Views Asked by At

My question is simple. I am following the official Vue documentation explaining v-model arguments in relation to forms in a child component. Here's my code:

(App.Vue)
<script>
import UserName from './UserName.vue'

export default {
  components: { UserName },
  data() {
    return {
      firstLast: {
        first: 'John',
        last: 'Doe'
      }
    }
  }
}
</script>

<template>
  <h1>{{ firstLast.first }} {{ firstLast.last }}</h1>
  <UserName
    v-model:first-name="firstLast.first"
    v-model:last-name="firstLast.last"
  />
</template>
(UserName.vue)
<script>
export default {
  props: {
      firstName: String,
    lastName: String
    },
  emits: ['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>

The above two files work! John Doe is displayed on screen, and whatever you put in the inputs will change the value of the name.

My issue is that in UserName.vue, I am referencing two separate variables, "first" and "last", when I would rather be referencing an object with both properties inside.

How do I achieve this?

1

There are 1 best solutions below

1
yoduh On BEST ANSWER

If the modelValue is changed to an object, then the inputs will need to emit an updated object.

App.vue

<template>
  <h1>{{ firstLast.first }} {{ firstLast.last }}</h1>
  <UserName v-model="firstLast" />
</template>

UserName.vue

<script>
export default {
  props: {
    modelValue: Object
  },
  emits: ['update:modelValue']
}
</script>

<template>
  <input
    type="text"
    :value="modelValue.first"
    @input="$emit('update:modelValue', { ...modelValue, first: $event.target.value })"
  />
  <input
    type="text"
    :value="modelValue.last"
    @input="$emit('update:modelValue', { ...modelValue, last: $event.target.value })"
  />
</template>

This becomes trivial if you use the newer Composition API and <script setup> which gives access to defineModel macro

Using Composition API

UserName.vue

<script setup>
const firstLast = defineModel()
</script>

<template>
  <input
    type="text"
    v-model="firstLast.first"
  />
  <input
    type="text"
    v-model="firstLast.last"
  />
</template>