Subscribe on changes!

We need one useListeners to get all the event functions passed in by the parent component

avatar
Feb 15th 2023

What problem does this feature solve?

When I design a general purpose component, I want the component to have a basic default behavior without any properties or events, meaning that you can even give the component no properties or events and it will work. It would be nice to pass a property or event to a component when you think it needs to modify a part of its behavior, and then override the default behavior of its corresponding part. Unfortunately when I was designing this component I found that once I defined the list of events held by the current component via defineEmits, I couldn't get the event function passed by the parent in the current component, which made my attempt to override the default behavior of the events impossible

Of course I knew there had been offers early on, but I checked them back and forth and saw that this feature was not taken seriously, so I was hoping to show off my own scenarios with a new feature suggestion and hope vue would consider opening up useListeners to give developers more room to play

What does the proposed API look like?

<style lang="scss">
.bo-dialog {
}
</style>

<template>
    <el-dialog
        v-model="state.vis"
        :close-on-click-modal="false"
        class="bo-dialog"
        title="Tips"
        :align-center="true"
        :draggable="true"
        v-bind="$attrs"
        :style="{minWidth: props.minWidth, maxWidth: props.maxWidth}"
        width="30%"
    >
        <template #header="slot_props_header">
            <slot name="header" v-bind="slot_props_header"></slot>
        </template>

        <template #default="slot_props_default">
            <slot name="default" v-bind="slot_props_default">
                <span>This is a message</span>
            </slot>
        </template>

        <template #footer="slot_props_footer" v-if="!props.hiddenFooter">
            <slot name="footer" v-bind="slot_props_footer">
                <div class="dialog-footer">
                    <el-button @click="methods.cancel_handle">{{ props.btnCancelText }}</el-button>
                    <el-button type="primary" @click="methods.confirm_handle">
                        {{ props.btnConfirmText }}
                    </el-button>
                </div>
            </slot>
        </template>

    </el-dialog>
</template>
<script lang="ts" setup>

const emit = defineEmits(["confirm","cancel"]);
//
const listeners = useListeners();

const methods = {
    cancel_handle ()
    {
        emit("cancel")
        !listeners.onCancel && dialog_close(); // The default behavior is provided here

    },
    confirm_handle ()
    {
        emit( "confirm" );
        !listeners.onConfirm && dialog_confirm(); // The default behavior is provided here
    },
};

</script>
avatar
Feb 19th 2023

All listeners are actually props with on prefix. For example, @click listener becomes onClick prop. You can use such props even without using defineEmits.

In that case you can do something like this:

const props = defineProps<{
  onConfirm: () => void
}>()

const handleConfirm = () => {
  if (props.onConfirm) {
    props.onConfirm()
  } else {
    // do some default actions
  }
}

You can even pass async listeners and use them as awaitable hooks, which can't be achieved with emit (as far as I know).

avatar
Feb 20th 2023

All listeners are actually props with on prefix. For example, @click listener becomes onClick prop. You can use such props even without using defineEmits.↳

In that case you can do something like this:↳

const props = defineProps<{
  onConfirm: () => void
}>()

const handleConfirm = () => {
  if (props.onConfirm) {
    props.onConfirm()
  } else {
    // do some default actions
  }
}

You can even pass async listeners and use them as awaitable hooks, which can't be achieved with emit (as far as I know).↳

I know this method can get around this issue but I don't want to use it because it's not a uniform use of events and if I have to use this method to do my own stuff then I really need official support for useListeners. In fact due to the lack of this api, I've given up on 0 configuration design for components for now

avatar
Mar 24th 2023

Agree! In vue2,users can get all registered events functions from parent by _events. But now I have to declare v-bind="attrs" in parent component, for that child component can get access to events name called onXXX. It makes complex to use component

avatar
Mar 31st 2023

Is it possible for useListener to be supported @yyx990803

avatar
May 8th 2023

You can do this

<script lang="ts" setup>
import { useAttrs } from 'vue'
// const emit = defineEmits(["confirm","cancel"]);
const attrs = useAttrs();

const methods = {
    cancel_handle ()
    {
        
        (attrs.onCancel || dialog_close)()
    },
    confirm_handle ()
    {
        (attrs.onConfirm || dialog_confirm)()
    },
};

</script>
avatar
Jun 14th 2023

mark