Reputation: 893
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
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
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.
// inside script setup
const somethingBus = useEventBus<string>('something');
const listener = (event: string) => {
console.log(`news: ${event}`)
}
const unsubscribe = somethingBus.on(listener)
// 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