Tutorial

How To Add `v-model` Support to Custom Vue.js Components

JavaScriptVue.js

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

Introduction

The v-model directive is one of the few directives that comes bundled with Vue.js. This directive allows for two-way data binding between our data and views.

With two-way data binding, when we update our data via input fields or other controls, we can modify the DOM without having to do DOM work.

In this article you’ll explore how this directive works and use it for your own components.

How v-model works internally

From our knowledge of HTML, we know that input, select, textarea are the main ways we feed data to our application.

For v-model to work, it expects the element or component in question to receive a prop (default is value) and also emit an event (default is input.)

Depending on the element, Vue decides how to listen and handle the data. For input elements, you might use v-model like this:

<input v-model="email" />

v-model translates to this:

<input :value="email" @input="e => email = e.target.value" />

Vue uses this expansion to handle textarea, select and some other input types.

For radio buttons and checkboxes, Vue uses their checked prop and listens for their change event.

For elements like select tags and checkboxes that can accept multiple values, Vue will automatically return an array of selected values.

How to add v-model to custom components

To let our component support v-model two-way binding, the component needs to accept a value prop and emit an input event.

Let’s create a sample component called basic-input. We’ll use Vue’s single file component:

<template>
  <input @input="handleInput" />
</template>

<script>
export default {
  prop: ['value'],
  data () {
    return {
      content: this.value
    }
  },
  methods: {
    handleInput (e) {
      this.$emit('input', this.content)
    }
  }
}
</script>

To support v-model, the component accepts a value prop and emits an input event.

Use the component like this:

<basic-input v-model="email" />

Customizing v-model prop and event

Let’s take it a step further. We might not want to use the default event and prop needed to add v-model support to our components. Thankfully, Vue allows us to customize it.

To customize the event and prop, we add a model property to our component and define the new values like this:

export default {
  prop: ['hidden'],
  model: {
      prop: 'hidden',
      event: 'blur'
  }
  methods: {
      handleInput (value) {
          this.$emit('blur', value)
      }
  }
}

This time, when you use the component like this:

<basic-input v-model="email" />

Vue will automatically convert it into:

<basic-input :hidden="email" @blur="e => email = e.target.value" />

With this in place, you can avoid conflicts when defining your component’s prop and events.

Using v-model on ContentEditable

A content editable element is a div or similar element that can be configured to work as an input.

We define content editable elements by adding the contenteditable attribute to the element:

<div class="editor" contenteditable="contenteditable"></div>

You’ll use content editable elements for WYSIWYG editors as they are easier to work with and are supported by a large amount of browsers.

v-model will work on content editable elements, but you need to explicitly use the content of the element, or the content will not be emitted.

To emit the content, you need to grab the innerText or innerHTML of the div. So, our updateInput method needs to look like this

updateInput () {
  this.$emit('input', this.$el.innerText)
}

You can also use the content of a ref instead of the root element’s content.

With this in place, v-model will work for content editable elements. You could also update this.content in the updateInput method.

Conclusion

Now that you have seen how to use v-model with custom Vue components, go build or refactor your components that require the use of v-model.

Creative Commons License