Home > Enterprise >  How to pass all events to far ancestor component in vuejs?
How to pass all events to far ancestor component in vuejs?

Time:07-18

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" />

demo

  • Related