How to handle multiple v-model bindings in Vue
Handling multiple v-model bindings in Vue enables complex component APIs with multiple synchronized properties for advanced form controls and interactive components. As the creator of CoreUI, a widely used open-source UI library, I’ve implemented multiple v-model bindings in sophisticated components like date range pickers and multi-select controls. From my expertise, the most effective approach is using named v-model directives with defineModel for clean component APIs. This method provides multiple two-way data binding channels while maintaining clear separation of concerns for different properties.
Use named v-model
directives with defineModel
to handle multiple synchronized properties.
<!-- UserEditor.vue (child component) -->
<template>
<div class="user-editor">
<input
v-model="firstName"
placeholder="First Name"
@input="updateFirstName">
<input
v-model="lastName"
placeholder="Last Name"
@input="updateLastName">
<input
v-model="email"
type="email"
placeholder="Email"
@input="updateEmail">
<select v-model="role" @change="updateRole">
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="moderator">Moderator</option>
</select>
</div>
</template>
<script setup>
// Define multiple v-models
const firstName = defineModel('firstName', { default: '' })
const lastName = defineModel('lastName', { default: '' })
const email = defineModel('email', { default: '' })
const role = defineModel('role', { default: 'user' })
const updateFirstName = () => {
// Additional validation or formatting logic
firstName.value = firstName.value.trim()
}
const updateLastName = () => {
lastName.value = lastName.value.trim()
}
const updateEmail = () => {
email.value = email.value.toLowerCase().trim()
}
const updateRole = () => {
// Role change logic
console.log('Role changed to:', role.value)
}
</script>
<!-- Parent component usage -->
<template>
<UserEditor
v-model:first-name="user.firstName"
v-model:last-name="user.lastName"
v-model:email="user.email"
v-model:role="user.role" />
<div>
<h3>User Data:</h3>
<p>Name: {{ user.firstName }} {{ user.lastName }}</p>
<p>Email: {{ user.email }}</p>
<p>Role: {{ user.role }}</p>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const user = reactive({
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
role: 'user'
})
</script>
Named v-model directives use the v-model:propertyName
syntax to bind specific properties. The defineModel
composable creates reactive references that automatically sync with parent component data. Each v-model creates a two-way binding channel for independent property synchronization.
Best Practice Note:
This is the same multiple v-model approach we use in CoreUI Vue components for complex form controls. Use descriptive names for each v-model binding and provide default values to ensure robust component initialization and prevent undefined states.