From Vue 2 to Vue 3: Multi v-model bindings

·

5 min read

In our last lesson, we went over the most common way to use v-model in a Vue 3 application, and we looked at the changes that the default bindings for input and event listeners bring. These are things we need to know in order to use v-model effectively.

It is now time to go deep into one of the advanced changes that Vue 3 brings in terms of v-model functionality — multi v-model bindings into a single component.

v-model: Not one, but many

A really interesting feature of Vue 3’s v-model is the ability to add multiple v-model bindings to a single component instance.

Have you ever created a complex component that $emits an object as the payload, and inside the object you have a couple of different properties? Maybe you had to pass the parent a lot of information, which forced you to have those properties? Wouldn’t it have been easier if you could have just bound to each of them separately?

Although this approach is fairly common, it brings a bit of overhead in understanding what a component is doing or what you’re supposed to receive out of a v-model payload. Instead of being able to quickly see what’s happening in the template of a component, you have to dig into the function that receives the payload and read through the implementation to figure out where each of the properties of the object are going to be used.

In Vue 3, with the addition of the multi v-model binding, a complex payload is no longer needed.

In order to demonstrate multi v-model capabilities, and to better learn how it works, we are going to create a component called SalutationName. This component will have two inputs inside of it — a select element that will allow the user to select their desired salutation, and an input for them to type in their name.

We’ll start out with the template, by adding these two new fields, value bindings, and some attributes.

📃SalutationName.vue

<template>
    <div>
      <select name="salutation">
        <option value="">-</option>
         <option
          v-for="item of salutations"
          :value="item"
          :key="item"
          :selected="salutation === item"
        >
          {{ item }}
        </option>
      </select>

      <input
        :value="name"
        type="text"
        name="name"
      />
    </div>
</template>

Now that we have our markdown ready, let’s take a look at our script. We need to create a salutations array to populate that dropdown, plus declare a couple of props that we can bind to the values we already declared.

📃SalutationName.vue

<script>
const salutations = [
  'Ms.',
  'Mrs.',
  'Miss',
  'Mx.',
  'Dr.'
]

export default {
  props: {
    salutation: {
      type: String,
      default: ''
    },
    name: {
      type: String,
      default: ''
    }
  },
  setup () {
    return {
      salutations
    }
  }
}
</script>

We declare two props, salutation and name. Both will be of type String with an empty string as a default value.

Notice that both these props are already binding to their respective elements in the template. The input through the value binding, and the select through the selected binding in the option loop.

In the setup function of our component, we are going to return the salutations array that we declare at the top of the script tag, so that the select element can v-for loop through it and populate all the options.

Finally, let’s add $emit calls for each of the inputs to broadcast their respective events.

📃SalutationName.vue

<template>
  <div>
    <select 
            name="salutation" 
            @change="$emit('update:salutation', $event.target.value)"
        >
      <option value="">-</option>
      <option
        v-for="item of salutations"
        :value="item"
        :key="item"
        :selected="salutation === item"
      >
        {{ item }}
      </option>
    </select>

    <input
      :value="name"
      @input="$emit('update:name', $event.target.value)"
      type="text"
      name="name"
    />
  </div>
</template>

<script>
const salutations = [
  'Ms.',
  'Mrs.',
  'Miss',
  'Mx.',
  'Dr.'
]

export default {
  props: {
    salutation: {
      type: String,
      default: ''
    },
    name: {
      type: String,
      default: ''
    }
  },
  setup () {
    return {
      salutations
    }
  }
}
</script>

Notice that we are using the update:X format for Vue 3’s v-model binding syntax that we learned in the last lesson.

For the select element, since we’re binding it to the prop salutation, we will emit update:salutation.

For the input element, since we’re binding it to the prop name, we will emit update:name.

Finally, we can import this new component anywhere in our app to use it.

📃App.vue

<template>
  <div id="app">
    <SalutationName
      v-model:salutation="form.salutation"
      v-model:name="form.name"
    />

    <pre>{{ form }}</pre>
  </div>
</template>

<script>
import { reactive } from 'vue'
import SalutationName from './components/SalutationName'

export default {
  name: 'App',
  components: {
    SalutationName
  },
  setup () {
    const form = reactive({
      salutation: '',
      name: ''
    })

    return {
      form
    }
  }
}
</script>

Take a closer look at the template where SalutationName is being used. Did you notice the double v-model binding?

📃App.vue

[...]
<SalutationName
  v-model:salutation="form.salutation"
  v-model:name="form.name"
/>
[...]

The first v-model declaration is going to provide a two way binding from the property salutation of SalutationName to our state form.salutation.

The second v-model declaration is going to provide a two-way binding from the property name of SalutationName to our state form.name.

We can now open this example in our browser. The result is a fully functional double v-modeled component!

image.png

Coming up next

In this lesson we learned how to make a component ready for multi v-model bindings, and how to bind to an instance of this component with multi v-modelS.

In the next lesson, we’re going to take a look at another cool advanced feature of Vue 3’s v-model, the ability to create our own custom model modifiers.

See you in the next lesson!