Easy Tutorial
❮ Vue Tutorial Vue Custom Directive ❯

Vue.js Components - Custom Events

Parent components pass data to child components using props, but if a child component needs to send data back, custom events are required!

We can use v-on to bind custom events. Each Vue instance implements the Events interface, which means:

Additionally, the parent component can directly listen for events triggered by the child component using v-on where the child component is used.

In the following example, the child component is completely decoupled from the outside. All it does is trigger an internal event that the parent component is interested in.

Example

<div id="app">
    <div id="counter-event-example">
      <p>{{ total }}</p>
      <button-counter v-on:increment="incrementTotal"></button-counter>
      <button-counter v-on:increment="incrementTotal"></button-counter>
    </div>
</div>

<script>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementHandler: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
</script>

If you want to listen for a native event on the root element of a component, you can use the .native modifier on v-on. For example:

<my-component v-on:click.native="doTheThing"></my-component>

Data Must Be a Function

In the above example, you can see that the data in the button-counter component is not an object but a function:

data: function () {
  return {
    count: 0
  }
}

This ensures that each instance maintains an independent copy of the returned object. If data were an object, it would affect other instances, as shown below:

Example

<div id="components-demo3" class="demo">
    <button-counter2></button-counter2>
    <button-counter2></button-counter2>
    <button-counter2></button-counter2>
</div>

<script>
var buttonCounter2Data = {
  count: 0
}
Vue.component('button-counter2', {
    /*
    data: function () {
        // data option is a function, components do not affect each other
        return {
            count: 0
        }
    },
    */
    data: function () {
        // data option is an object, affects other instances
        return buttonCounter2Data
    },
    template: '<button v-on:click="count++">Clicked {{ count }} times.</button>'
})
new Vue({ el: '#components-demo3' })
</script>

Custom Component v-model

The v-model on a component defaults to using a prop named value and an event named input.

<input v-model="parentData">

Is equivalent to:

&lt;input 
    :value="parentData"
    @input="parentData = $event.target.value"
>

This is a custom component example for tutorialpro-input, where the initial value of num in the parent component is 100, and changing the value in the child component updates num in the parent component in real-time:

Example

<div id="app">
    <tutorialpro-input v-model="num"></tutorialpro-input>
    <p>The entered number is: {{num}}</p>
</div>
<script>
Vue.component('tutorialpro-input', {
    template: `
    <p>   <!-- Includes an event named input -->
      &lt;input
       ref="input"
       :value="value" 
       @input="$emit('input', $event.target.value)"
      >
    </p>
    `,
    props: ['value'], // Prop named value
})

new Vue({
    el: '#app',
    data: {
        num: 100,
    }
})
</script>

Since v-model by default passes value rather than checked, for components like checkboxes or radio buttons, we need to use the model option. The model option can specify the current event type and the props being passed.

Example

<div id="app">
    <base-checkbox v-model="lovingVue"></base-checkbox> 
     <div v-show="lovingVue"> 
        I will show up if the checkbox is checked. 
    </div>
</div> 
<script>
// Register
Vue.component('base-checkbox', {

  model: {
    prop: 'checked',
    event: 'change'  // onchange event
  },
  props: {
    checked: Boolean
  },

  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})
// Create root instance
new Vue({
  el: '#app',
  data: {
    lovingVue: true
  }
})
</script>

In this example, the value of lovingVue is passed to the checked prop, and when the <base-checkbox> triggers the change event, the value of lovingVue is also updated.

❮ Vue Tutorial Vue Custom Directive ❯