Silly this is so hard to do. I ended up going to the Vue internals to look at how they determine what the event handler is. I got this code from https://github.com/vuejs/core/blob/main/packages/runtime-core/src/componentEmits.ts#L166 which defines the behavior of how a component emits an event and adapted it.
The below code looks at if there is a function that would be called if you invoked emit and returns true if there is. It's likely only compatibility with the version of Vue I took it from which as of writing this is 3.4.
Edit
Works with Vue 3.5.11
This should work in theory with all event types and could be adapted further to return what ones are there and what ones aren't using the instances emitOptions.
import { getCurrentInstance, toHandlerKey } from 'vue';
import { hyphenate, camelize } from '@vue/shared';
/**
* Determines if the current component has a listener for a specific event
*
* See https://github.com/vuejs/core/blob/main/packages/runtime-core/src/componentEmits.ts#L166
*
* @param event Name of the event you wish to look for, eg: 'click'
*/
export function hasEmitListener(event) {
const instance = getCurrentInstance();
const props = instance?.vnode.props;
const isModelListener = event.startsWith('update:')
if (!props) return false
let handler =
props[toHandlerKey(event)] ||
// also try camelCase event handler (#2249)
props[toHandlerKey(camelize(event))];
if (!handler && isModelListener) {
handler = props[toHandlerKey(hyphenate(event))]
}
return Boolean(handler);
}
Example code
const { getCurrentInstance, toHandlerKey } = Vue;
const { hyphenate, camelize } = Vue;
/**
* Determines if the current component has a listener for a specific event
*
* See https://github.com/vuejs/core/blob/main/packages/runtime-core/src/componentEmits.ts#L166
*
* @param event Name of the event you wish to look for, eg: 'click'
*/
function hasEmitListener(event) {
const instance = getCurrentInstance();
const props = instance.vnode.props;
if(!props) {
return false;
}
const isModelListener = event.startsWith('update:')
let handler =
props[toHandlerKey(event)] ||
// also try camelCase event handler (#2249)
props[toHandlerKey(camelize(event))];
if (!handler && isModelListener) {
handler = props[toHandlerKey(hyphenate(event))]
}
return Boolean(handler);
}
const Button = {
name: 'Button',
inheritAttrs: false,
components: {
},
data() {
return {
hasClick: hasEmitListener('click'),
};
},
template: `
<button v-if="hasClick" @click="$emit('click', $event)">
Click Me! 😊
</button>
<button v-else>Nothing to do 😔</button
`,
};
const App = {
name: 'App',
components: {
Button,
},
data() {
return {
};
},
template: `
<div>
Button 1:
<Button @click="() => { console.log('hello'); }"></Button>
<br />
Button 2:
<Button></Button>
</div>
`,
};
const app = Vue.createApp({
render: () => Vue.h(App),
});
app.mount('#app');
<script src="https://unpkg.com/[email protected]/dist/vue.global.prod.js"></script>
<div id="app">
</div>
Composition API
When using composition API, be sure to set the values during the setup flow or in a lifecycle hook like onMounted. This is because getCurrentInstance will probably be empty at the time you handle your emit and maybe need this value.
onDelete emit is passed by the parent component? in this case onlycolorandsquareare passed by parent<Badge square @onDelete="onDeleteOption(selected_option)">Test</Badge>onDeletelistener in this wayv-if="$listeners.onDelete"$listenerswas removedv-if="$attrs.onOnDelete"