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:
- Use
$on(eventName)
to listen for an event - Use
$emit(eventName)
to trigger an event
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:
<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 -->
<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.