The question is similar to this one, except, the emit event is not going to the grand parent, but a further one.
How to pass all events to parent in VueJS
The way I am trying to emit all events up the stack is this way:
<View_5 /> <!-- does an emit event -->
<View_4 v-on="$attrs" /> <!-- pass all events to parent -->
<View_3 v-on="$attrs" /> <!-- pass all events to parent. But it breaks here. -->
At View_3, it doesnt pass the events to its parents. What I'm i doing wrong?
[EDIT] - Here is a link to a sample project on stackblitz
Click the black square, and you can see the text changes. This works because it bubbled to the a "go" event from components D -> to C -> to B -> to A, using the old fashion way. Now how do i make it so that components C and B do NOT specifically look for the "go" event, but simply pass all events up to component A?
CodePudding user response:
Personally, I'm not a big fan of emitting the events up the stack if the event is not emitted to a direct parent and should go way up, exactly for the reasons you mentioned: it may be hard to follow where exactly things break. But that's just my opinion. What I do like to do in such cases is to use EventBus.
Essentially, an event bus is a Vue.js instance that can emit events in one component, and then listen and react to the emitted event in another component directly — without the help of a parent component.
First create an eventBus.js
file (I like to store mine in a utils directory):
import Vue from 'vue'
const EventBus = new Vue()
export default EventBus
In your child component:
import EventBus from '@/utils/eventBus
export default {
//rest of your setup
methods: {
myMethodHandler() {
EventBus.$emit('myEvent')
}
}
}
And then in the grand parent components (the component that has to receive the event):
import EventBus from '@/utils/eventBus
export default {
//rest of your setup
created() {
EventBus.$on('myEvent', () => {
// your business logic here
})
}
}
Of course you can give the events whatever name that you like and then listen to the same event. And you can pass payload if needed - just pass it in the emitted event right after the event name and receive them in the EventBus callback function:
EventBus.$emit('myEvent', someString, someObject)
//...
EventBus.$on('myEvent', (someStringPayload, someObjectPayload) => {
// do your thing
})
The examples above are for Vue2. For Vue3, according to the official doc, you can use a third party library, such as mitt or tiny-emitter.
CodePudding user response:
v-on="$attrs"
should be v-bind="$attrs"
.
$attrs
contains key-value pairs of attributes and their values. For @go="handler"
, $attrs
would be { onGo: handler }
, where the on
-prefix is automatically to the key.
v-on="obj"
creates event handlers for the key-value pairs in obj
. For instance, v-on="{ foo: handler }
creates a listener for the foo
event that runs handler()
.
Given the above, v-on="$attrs"
in your case would incorrectly create a listener for the onGo
event (when it really should be for the go
event). Further, each v-on="$attr"
in the nested components would prepend on
to the name at each nested level, leading to onOnOnGo
in DD.vue
.
Solution
Use v-bind="$attrs"
to correctly forward the v-on
directive:
<!-- AA.vue -->
<BB @click="onClick" />
<!-- BB.vue -->
<CC v-bind="$attrs" />
<!-- CC.vue -->
<DD v-bind="$attrs" />
<!-- DD.vue -->
<button v-bind="$attrs" />