baitendbidz
baitendbidz

Reputation: 893

Is it possible to forward specific events from child components to the own parent component?

I have a simple component raising events whenever I click on a button.

InnerCompo.vue

<template>
  <v-btn @click="emit('something-happened')">Click me</v-btn>
</template>

<script setup lang="ts">
const emit = defineEmits<{ (e: 'something-happened'): void }>()
</script>

This component is consumed by a container component that doesn't care for the event but its own parent component does. Currently, I listen for the event and raise it again like this-

OuterComp.vue

<template>
  <inner-comp @something-happened="onSomethingHappened" />
</template>

<script setup lang="ts">
import InnerComp from "./InnerComp.vue";
  
const emit = defineEmits<{ (e: 'something-happened'): void }>()

function onSomethingHappened() {
  emit("something-happened");
}
</script>

The consuming parent handles the event-

App.vue

<template>
  <v-app>
    <v-main>
      <outer-comp @something-happened="onSomethingHappened" />
    </v-main>
  </v-app>
</template>

<script setup lang="ts">
import OuterComp from "./OuterComp.vue";

function onSomethingHappened() {
  console.log("something happened");
}
</script>

Is there a way to simplify the code in OuterComp?
This component needs some boilerplate code to forward the event back to its parent. I know that this component must define the emits for proper TS support but maybe there is a shorthand syntax to "bubble" the event e.g.

<inner-comp @something-happened.direct-forward="onSomethingHappened" />

For reproduction purposes- See here

Upvotes: 1

Views: 90

Answers (2)

tao
tao

Reputation: 90068

You're probably looking for v-bind="$attrs". In Vue 3, Vue events are attributes prefixed with on, they are no longer separately declared as $listeners, as in Vue 2 - documented here):

OuterComp.vue:

<template>
  <div>
    <inner-comp v-bind="$attrs" />
  </div>
</template>
<script>
  export default {
    inheritAttrs: false
  }
</script>

inheritAttrs: false tells Vue not to bind attributes to the outer <div>. And v-bind="$attrs" binds them to one (or more) inner element(s), without having to specify each attribute individually.


Note: if you have some attributes which you want to pass and some which you don't want passed to children, you can group attributes in objects:

  <inner-comp v-bind="$attrs.foo" />

, where foo is an object:

  <outer-comp :foo="{ onSomethingHappened }" />

Upvotes: 1

Leif Marcus
Leif Marcus

Reputation: 623

There is a nice library for Vue 3 called VueUse which gives you really nice utility composables. They have a composable called useEventBus which you can use independent of the component structure.

App.vue

// inside script setup
const somethingBus = useEventBus<string>('something');

const listener = (event: string) => {
  console.log(`news: ${event}`)
}

const unsubscribe = somethingBus.on(listener)

InnerComp.vue

// inside script setup
const somethingBus = useEventBus<string>('something');

const handleClick = () => {
  somethingBus.emit('Some Value')
}

When using with typescript you can define a symbol with the type EventBusKey that can be used as the event bus key. See the documentation for more details

Upvotes: 1

Related Questions