I'm pretty new to Vue.js, and I use ES6 syntax with vue-class-component
. I'm having a problem while trying to emit an event from a child to its parent.
I followed the logic of the default Vue.js syntax but can't seem to have my parent catch an event emitted by the the child. Code:
Child Component
I attached a click event listener on every <li>
, which calls a function that emits an event. The event listener is defined on the parent.
export default class HorizontalNavigation {
handleClick (e) {
e.preventDefault();
// console.log(e.target.dataset.section);
this.$emit('ChangeView', e.target.dataset.section);
}
render (h) {
return (
<div class={ style.horizontalNavigation } >
<div class='sections-wrapper'>
<div class={ style.sections }>
<ol>
<li><a on-click={ this.handleClick } href='#1' data-section='1' class='selected'>We are</a></li>
<li><a on-click={ this.handleClick } href='#2' data-section='2'>Services</a></li>
<li><a on-click={ this.handleClick } href='#3' data-section='3'>Cases</a></li>
<li><a on-click={ this.handleClick } href='#4' data-section='4'>Studio</a></li>
<li><a on-click={ this.handleClick } href='#5' data-section='5'>Career</a></li>
<li><a on-click={ this.handleClick } href='#6' data-section='6'>Contact</a></li>
</ol>
<span class='filling-line' aria-hidden='true'></span>
</div>
</div>
</div>
);
}
}
Parent Component
export default class ViewsContainer {
ChangeView (data) {
console.log(data);
}
render (h) {
return (
<div class={ style.restrictOverflow } >
<HorizontalNavigation />
<main class={ style.viewsContainer } on-ChangeView={ this.ChangeView } >
<SingleView dataSection='weare' id='1' class='selected' />
<SingleView dataSection='services' id='2' />
<SingleView dataSection='cases' id='3' />
<SingleView dataSection='studio' id='4' />
<SingleView dataSection='career' id='5' />
<SingleView dataSection='contact' id='6' />
</main>
</div>
);
}
}
Following the vue.js syntax, I should be able to listen to the child's event from the parent, by emitting the event from the element, but the parent doesn't seem to catch the event.
Where am I going wrong?
Whenever the text changes, we want to emit an event with the uppercased value of our input. Instead of calling $emit from our template, we can call a component method instead. Inside, we can call this. $emit and pass it our value.
To call a child's function from a parent component in React: Wrap the Child component in a forwardRef . Use the useImperativeHandle hook in the child to add a function to the Child . Call the Child's function from the Parent using the ref, e.g. childRef.
The best way to force Vue to re-render a component is to set a :key on the component. When you need the component to be re-rendered, you just change the value of the key and Vue will re-render the component.
You need to $emit
on the view model you want to receive your $emit
, use a bus
or use https://vuex.vuejs.org/.
The easiest way, is to simply $emit
from the child directly to the parent component:
this.$parent.$emit('ChangeView', e.target.dataset.section);
This is OK, but if you have a long chain of components you need to $emit
to each $parent
in the chain.
You can use Vue to create a global event bus very simply. You create an instance of Vue in your main component, then all other components in your application can emit
events to it, and use on
to react to those events.
var bus = new Vue({});
var vm = new Vue({
methods: {
changeView() {
bus.$emit('ChangeView', e.target.dataset.section);
}
});
It is also possible to emit to $root
but this isn't recommended. However, if your app
does get quite complex you may want to consider using Vues own state management system, vuex, instead.
You can listen for the $emit
in the viewmodel using $on
and listening for the specific event:
var vm = new Vue({
created() {
this.$on('ChangeView', section => {
console.log(section);
});
}
);
This would also be similar is you were using the bus, but you would just reference the bus instead:
bus.$on('ChangeView', ...);
And you may also use it directly in your html using v-on
:
<div v-on:ChangeView="doSomething"></div>
In Vue, you can communicate from children up to parent components by emitting events. The child compoment "emits" an event with $emit
& the parent component "listens" for it like so:
Child Component:
<template>
<div>
<button @click="this.handleClick">Click me!</button>
</div>
</template>
<script>
export default {
name: "BaseButton",
methods: {
handleClick: function() {
this.$emit("clickFromChildComponent");
}
}
};
</script>
<style scoped>
</style>
Parent Component:
<template>
<div class="home">
<h1>Passing a event from child up to parent!</h1>
<BaseButton @clickFromChildComponent="handleClickInParent"/>
</div>
</template>
<script>
import BaseButton from "./BaseButton";
export default {
components: {
BaseButton
},
name: "Home",
methods: {
handleClickInParent: function() {
alert('Event accessed from parent!');
}
}
};
</script>
<style scoped>
</style>
You can find a working sample code implementation here & use it in your own context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With