We need one useListeners to get all the event functions passed in by the parent component
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>
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).
All listeners are actually props with
on
prefix. For example,@click
listener becomesonClick
prop. You can use such props even without usingdefineEmits
.↳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
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
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>