// Tutorial //

How To Add Two-Way Data Binding to Custom Components in Vue.js

Published on January 18, 2017 · Updated on October 28, 2020
Default avatar
By Joshua Bemenderfer
Developer and author at DigitalOcean.
How To Add Two-Way Data Binding to Custom Components in Vue.js

Introduction

In this article, you will learn about how to add the v-model directive for custom components in Vue.js. While the v-model directive is a powerful asset to add two-way data binding in vanilla Vue.js components, support for custom components isn’t as exercised.

Prerequisites

An understanding of two-way data binding in Vue.js is suggested but not required. To learn more about two-way data binding, check out our How To Use v-model for Two-Way Binding in Vue.js tutorial.

Implementing the v-model Directive

To understand how to implement v-model support in your components, you need to understand how it works under the hood. The v-model="prop" value is shorthand for :value="prop" @input="prop = arguments\[0\]".

As such, to make your component compatible with the v-model directive, it must accept the :value and @input attributes to add and emit a value when a user interacts with your Vue.js application.

In your DatePicker.vue file, create a custom component that accepts a month and year value in an object. The :value and @input attributes will represent the values referenced in your component. Set a template with inputs for your month and year values:

DatePicker.vue
<template>
  <div class="date-picker">
    Month: <input type="number" ref="month-picker" :value="value.month" @input="updateDate()"/>
    Year: <input type="number" ref="year-picker" :value="value.year" @input="updateDate()"/>
  </div>
</template>

<script>
export default {
  props: ['value'],

  methods: {
    updateDate() {
      this.$emit('input', {
        month: +this.$refs.monthPicker.value,
        year: +this.$refs.yearPicker.value
      })
    }
  }
};
</script>

Within the script tag, your custom method updateDate() applies the .$emit() public instance property to update the values in month and year based on user input.

To use your custom component in other Vue.js files, set the v-model directive to bind and add values from your month and year properties in your WrapperComponent.vue file:

WrapperComponent.vue
<template>
  <div class="wrapper">
    <date-picker v-model="date-picker"></date-picker>
    <p>
      Month: {{date.month}}
      Year: {{date.year}}
    </p>
  </div>
</template>

Within the script tag, import your custom component and insert it into the components property. This will employ the functionality contained in your DatePicker component into your WrapperComponent.vue file:

WrapperComponent.vue
<script>
import DatePicker from './DatePicker.vue';

export default {
  components: {
    DatePicker
  },

  data() {
    return {
      date: {
        month: 1,
        year: 2017
      }
    }
  }
})
</script>

The data() model stores and returns the values in your month and year properties.

To review, two-way binding with custom components requires the attributes :value and @input to add and emit an updated date in one call.

Practicing v-model in Advanced Components

By using one or more computed properties, you can integrate data, such as strings, into a format input that elements can manage. This is often used with advanced custom components that handle a variety of potential input formats.

In your StringDatePicker.vue file, create a custom component that passes a string with the structure m/yyyy. Set your :value and @input attributes into your inputs to accept and update values from your custom component:

StringDatePicker.vue
<template>
  <div class="date-picker">
    Month: <input type="number" ref="monthPicker" :value="splitDate.month" @input="updateDate()"/>
    Year: <input type="number" ref="yearPicker" :value="splitDate.year" @input="updateDate()"/>
  </div>
</template>

By using a computed property, in this case splitDate, you can split and return the input string into an object with month and year properties:

[label StringDatePicker.vue] 
<script>
export default {
  props: ['value'],

  computed: {
    splitDate() {
      const splitValueString = this.value.split('/');

       return {
        month: splitValueString[0],
        year: splitValueString[1]
      }
    }
  },

  methods: {
    updateDate() {
      const monthValue = this.$refs.monthPicker.value;
      const yearValue = this.$refs.yearPicker.value;
      this.$emit('input', `${monthValue}/${yearValue}`);
    }
  }
};
</script>

The methods object applies the property updateDate() to emit the updated month and year from an input string. You can now import the StringDatePicker advanced custom component into another file and operate the v-model directive to bind and add your values from your month and year properties at once.

Conclusion

The v-model directive provides extra functionality when implemented in custom Vue.js components. You can also integrate two-way data binding into advanced custom components to manipulate data types into precise formats.

For further reading about Vue.js, check out the Vue.js topic page.


Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
3 Comments

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Thank you for this article! But - I do not understand why the v-model on the date-picker component is called “date-picker”? Wouldn’t that require and equally named attribute on the parent’s data object? But I only see it return a “date” object…

Hi sir,

I am using Vuelidate validation for my form, is there any tutorial to add it into a nested object which uses in a custom component?

This is one way. *Child only emits data to parent

How can the parent update the child data?