Frontend DevelopmentVueJs Tutorials

Vue 3 Manipulating v-model With The defineModel macro

Vue 3 Manipulating v-model With The defineModel macro

Two-way data binding is key to creating dynamic apps. Vue.js makes it easy to handle two-way model binding using the v-model directive. In Vue 3.4 the defineModel macro takes this further. It gives you more type safety and cleans up your code. 

 

 

v-model provides two-way data binding. What does that mean? Changes in the UI update the data, and changes in the data update the UI. v-model is actually shorthand. It combines passing a prop and emitting an event. This helps keep your components in sync.

Before defineModelv-model used props and $emit. This involved more code. You had to define a prop and emit an update event. This approach worked, but was a bit clunky. It had issues, like more code and potential type errors.

 

Basic usage:

MyForm.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script setup>
const nameModel = defineModel();
</script>
<template>
<p>
<label>Name</label>
<input type="text" v-model="nameModel" />
</p>
</template>
<script setup> const nameModel = defineModel(); </script> <template> <p> <label>Name</label> <input type="text" v-model="nameModel" /> </p> </template>
<script setup>
  const nameModel = defineModel();
</script>

<template>
  <p>
    <label>Name</label>
    <input type="text" v-model="nameModel" />
  </p>
</template>

Declaring a model simply by calling the defineModel() macro, and then passing it to the v-model directive. Now in parent component you can use v-model as well with the child component:

App.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script setup>
...
const data = ref('');
</script>
<template>
<MyForm v-model="data" />
</template>
<script setup> ... const data = ref(''); </script> <template> <MyForm v-model="data" /> </template>
<script setup>
  ...
  const data = ref('');
</script>

<template>
  <MyForm v-model="data" />
</template>

Now the data is synced in parent and child component. As we know previously to implement this without defineModel() we have to pass a prop and emit an event like so:

MyForm.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
</script>
<template>
<input
type="text"
:value="props.modelValue"
@input="emit('update:modelValue', ($event.target as HTMLInputElement).value)"
/>
</template>
<script setup> const props = defineProps(['modelValue']); const emit = defineEmits(['update:modelValue']); </script> <template> <input type="text" :value="props.modelValue" @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)" /> </template>
<script setup>
  const props = defineProps(['modelValue']);
  const emit = defineEmits(['update:modelValue']);
</script>

<template>
  <input
    type="text"
    :value="props.modelValue"
    @input="emit('update:modelValue', ($event.target as HTMLInputElement).value)"
  />
</template>

This approach is working, however you have to write more code and makes the code base more complicated especially if you want to handle multiple v-model in the same component. 

defineModel automatically creates the prop and event for v-model. No more manual work! It reduces the amount of code needed. The syntax is straightforward. 

 

Multiple v-model On The Same Component

With defineModel() macro we can handle multiple v-model on the same child component by specifying an argument to v-model i.e “v-model:argument“:

App.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script setup>
...
const name = ref('');
const email = ref('');
</script>
<template>
<MyForm v-model="name" v-model:email="email" />
</template>
<script setup> ... const name = ref(''); const email = ref(''); </script> <template> <MyForm v-model="name" v-model:email="email" /> </template>
<script setup>
  ...
  const name = ref('');
  const email = ref('');
</script>
<template>
  <MyForm v-model="name" v-model:email="email" />
</template>

In the child component specify the argument name to defineModel() as the first parameter:

MyForm.vue

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<script setup>
const nameModel = defineModel();
const emailModel = defineModel('email'); // defineModel with argument
</script>
<template>
<p>
<label>Name</label>
<input type="text" v-model="nameModel" />
</p>
<p>
<label>Email</label>
<input type="email" v-model="emailModel" />
</p>
</template>
<script setup> const nameModel = defineModel(); const emailModel = defineModel('email'); // defineModel with argument </script> <template> <p> <label>Name</label> <input type="text" v-model="nameModel" /> </p> <p> <label>Email</label> <input type="email" v-model="emailModel" /> </p> </template>
<script setup>
  const nameModel = defineModel();
  const emailModel = defineModel('email');    // defineModel with argument
</script>
<template>
  <p>
    <label>Name</label>
    <input type="text" v-model="nameModel" />
  </p>

  <p>
    <label>Email</label>
    <input type="email" v-model="emailModel" />
  </p>
</template>

Each property passed with v-model to the child component is synced with the parent as usual. This way we can create complex forms with little code.

 

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest


0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments