vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
45.63k stars 8k forks source link

Forwarding of emits through intermediate components between parent/grandchild like in Svelte #5917

Open Arturexe opened 2 years ago

Arturexe commented 2 years ago

What problem does this feature solve?

Svelte has this awesome feature, where you forward emits through intermediate components between parent/grandchild.

This feature would help managing emits, since components can get big and confusing, esp. with a lot of props/emits being passed around.

What does the proposed API look like?

GrandChild.vue:

<template>
    <GrandChild @click=click/>
</template>

<script setup>
const emit = defineEmits([
    'emitData'
])

const click = (data) => {
    emit('emitData', data)
}
</script>

Child.vue:

<template>
    <Child @emitData>
</template>

or

<template>
    <Child @emitData=$up>
</template>

or something more creative..., and it would be received by the parent just like it is now:

<template>
    <Parent @emitData=getData/>
</template>

<script setup>
const getData = (data) => {
    console.log(data)
}
</script>
MellKam commented 10 months ago

I don't know for sure, but it seems like toHandlers is what you need.

UPD: No, it doesnt work.

Here's a utility from shadcn-vue https://github.com/radix-vue/shadcn-vue/blob/ae81084f367b704615f6e6677c91cbae466c4978/apps/www/src/lib/utils.ts#L17-L34 that I've improved a bit and it works great for me.

import { camelize, getCurrentInstance, toHandlerKey } from "vue";

function useEmitAsProps<EventName extends string>(
    emit: (name: EventName, ...args: any[]) => void
) {
    const result: Record<string, any> = {};
    const vm = getCurrentInstance();
    if (!vm) return result;

    const events: EventName[] = Array.isArray(vm.type.emits)
        ? vm.type.emits
        : typeof vm.type.emits === "object"
        ? Object.keys(vm.type.emits)
        : [];

    if (!events.length) {
        console.warn(
            `No emitted event found. Please check component: ${vm.type.__name}`
        );
    }

    for (let i = 0; i < events.length; i++) {
        result[toHandlerKey(camelize(events[i]!))] = (...args: any) =>
            emit(events[i]!, ...args);
    }

    return result;
}