DEV Community

Blind Kai
Blind Kai

Posted on

Using v-model with objects in Vue3

Motivation

When I was using Vue2 along with vue-class-component and vue-property-decorator it was easy to synchronize v-models between components simply using @ModelSync(). When Vue 3 came out with its Composition API another way was needed to achieve the same result as if Class Component was used.

Implementation

If you're already familiar with the capabilities of Composition API, then simply use computed within setup to update the modelValue whenever it changes.

1) In child component define a model property with the default value

import { defineComponent } from 'vue';

<script>
export default defineComponent({
  name: 'FancyComponent',
  props: {
    modelValue: {           // Declare the property
      type: Object,
      default: () => ({}),  // Do not forget about default value
    },
  }
});
</script>
Enter fullscreen mode Exit fullscreen mode

2) In setup() define a computed property and expose it:

<template>
  <div>
    <input v-model="theModel.foo" />  <!-- Usage of model -->
  </div>
</template>

<script>
import { defineComponent, computed } from 'vue';

export default defineComponent({
  name: 'FancyComponent',
  emits: ['update:modelValue'], // The component emits an event
  props: {
    modelValue: {
      type: Object,
      default: () => ({}),
    },
  },
  setup(props, { emit }) {
    const theModel = computed({  // Use computed to wrap the object
      get: () => props.modelValue,
      set: (value) => emit('update:modelValue', value),
    });

    return { theModel };
  }
});
</script>
Enter fullscreen mode Exit fullscreen mode

3) In parent component use v-model directive:

<template>
  <FancyComponent v-model="someObject" />
</template>
Enter fullscreen mode Exit fullscreen mode

TypeScript

In the case of using TypeScript, there is one minor addition to the code above. PropType<T> is used it order to annotate the model type.

<script>
import { defineComponent, computed, PropType } from 'vue';

interface OurModelType {
  foo: string;
}

export default defineComponent({
  name: 'FancyComponent',
  emits: ['update:modelValue'],
  props: {
    modelValue: {
      type: Object as PropType<OurModelType>, // Type Annotation
      default: () => ({}),
    },
  },
  setup(props, { emit }) {
    const theModel = computed({
      get: () => props.modelValue,
      set: (value) => emit('update:modelValue', value),
    });

    return { theModel };
  }
});
</script>
Enter fullscreen mode Exit fullscreen mode

And it's all you need to know to pass reactive objects into your custom components as v-model.

Discussion (0)