I have my vue application using:
component-parent component that is made of component-child
inside component-parent I have buttons, when someone click a button I want to emit an event in order to be handled by vue and passed to another component
What I did so far:
var vm = new Vue({
el: '#app',
methods: {
itemSelectedListener: function(item){
console.log('itemSelectedListener', item);
}
}
});
Vue.component('component-child', {
template: ' <span v-on:click="chooseItem(pty )" >Button </span>',
methods: {
chooseItem: function(pty){
console.log(pty);
this.$emit('itemSelected', {
'priority' : pty
});
}
}
});
Vue.component('component-parent', {
template: '<component-child v-for="q in items" ></component-child>'
});
HTML:
<component-parent v-on:itemSelected="itemSelectedListener" ></component-parent>
It reaches my console.log(pty);
line but it seems that this.$emit('itemSelected'
wont get through:
console.log('itemSelectedListener', item); // this is not going to be called...
an hint?
should I bubble up the event from child->parent->Vue-instance? ( I also tried that but with no success)
There is one issue with your component-parent
template as it tries to render multiple child components. Vue usually requires a single root div inside components therefore you need to wrap it in a div or other tag.
<div>
<component-child v-for="q in items"></component-child>
</div>
A second thing to point out is that you emit an event from a child component which is 2 levels down and you listen to it in the root.
Root //but you listen to the event up here 1 level above
Component 1 //you should listen to the event here
Component 2 //your try to emit it from here
You have 2 options here. Either emit from component-child
listen to that event in component-parent
then propagate that event upwards. Fiddle https://jsfiddle.net/bjqwh74t/29/
The second option would be to register a global so called bus
which is an empty vue instance that you can use for such cases when you want communication between non child-parent components. Fiddle https://jsfiddle.net/bjqwh74t/30/
Usually between parent and child components you use the events directly by emitting from child and listening in parent with v-on:event-name="handler"
but for cases where you have more levels between components you use the second approach.
Doc link for the first case: https://v2.vuejs.org/v2/guide/components.html#Using-v-on-with-Custom-Events
Doc link for the second case: https://v2.vuejs.org/v2/guide/components.html#Non-Parent-Child-Communication
PS: prefer using kebab-case for event names which means you write with -
instead of capital letters. Writing with capital letters can result in weird situations where your event is not caught in the root.
For what it's worth you can use the browser's event API. It requires a little more scripting than Vue's built-in stuff, but it also gets you around these bubbling issues (and is about the same amount of code as creating a "bus", as in the accepted answer).
On child component:
this.$el.dispatchEvent(new CustomEvent('itemSelected', { detail: { 'priority' : pty }, bubbles: true, composed: true });
On parent component, in mounted
lifecycle part:
mounted() {
this.$el.addEventListener('itemSelected', e => console.log('itemSelectedListener', e.detail));
}
It's a little bit late but here's how I did it:
component-child:
this.$root.$emit('foobar',{...});
component-parent:
this.$root.$on('foobar')
In your child component, simply use $emit
to send an event to the $root
like this:
v-on:click="$root.$emit('hamburger-click')"
Then, in your parent component (eg: "App"), setup the listener in the Vue mounted
lifecycle hook like this:
export default {
<snip...>
mounted: function() {
this.$root.$on('hamburger-click', function() {
console.log(`Hamburger clicked!`);
});
}
}
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