Just a coder
Just a coder

Reputation: 16730

How to pass all events to far ancestor component in vuejs?

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?

Upvotes: -1

Views: 1766

Answers (2)

tony19
tony19

Reputation: 138696

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

Upvotes: 0

Igal
Igal

Reputation: 6103

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.

Upvotes: 2

Related Questions