Vue defineModel Usage
I recently needed to break a large form into smaller components in a Vue 3 application. I wanted to use defineModel
for 2-way data binding between the parent and child components. Here's how I did it.
The parent component needs to have all the data that it will be submitting to the server. It passes chunks of that data to child components using the v-model
attribute.
Child components create a model variable using defineModel
macro to create a reactive property that will be bound to the parent component's data. This allows for 2-way data binding, meaning that changes in the child component will update the parent's data and vice versa.
Here's a simplified example of breaking a form into two child components, each handling different parts of the form data.
<script setup>
import { ref } from 'vue'
import ChildComponent1 from './ChildComponent1.vue'
import ChildComponent2 from './ChildComponent2.vue'
const childData1 = ref({
name: '',
email: '',
})
const childData2 = ref({
contacts: [],
})
const submitForm = () => {
// Handle form submission logic here
console.log('Form submitted with:', {
childData1: childData1.value,
childData2: childData2.value,
})
const formData = {
...childData1.value,
...childData2.value,
}
// You can now send formData to your server
}
</script>
<template>
<div>
<form @submit.prevent="submitForm">
<ChildComponent1 v-model="childData1" />
<ChildComponent2 v-model="childData2" />
<button type="submit">Submit</button>
</form>
</div>
</template>
<script setup>
const model = defineModel()
</script>
<template>
<div>
<input v-model="model.name" type="text" placeholder="Name" />
<input v-model="model.email" type="email" placeholder="Email" />
</div>
</template>
<script setup>
import { ref } from 'vue'
const model = defineModel()
const newContact = ref('')
const addContact = () => {
model.value.contacts.push(newContact.value)
}
</script>
<template>
<div>
<ul class="mb-2">
<li v-for="(contact, idx) in model.contacts" :key="idx">{{ contact }}</li>
</ul>
<label for="contacts-input">New Contact</label>
<input
id="contacts-input"
v-model="newContact"
type="text"
placeholder="Contacts"
/>
<button @click="addContact">Add Contact</button>
</div>
</template>