Tutorial

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

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:

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.

Creative Commons License