Reputation: 9443
Assuming there's some <Form>
component. It can be called with a @cancel
event listener attached to it and if it's the case, I want to show the cancel button that triggers this event. If there's no @cancel
event, the cancel button should not be visible.
Is there a way to check if a component has event listener attached to it?
Currently I do:
<template>
<form>
<button v-if="cancelEventPassed" @click="$emit('cancel')">Cancel</button>
</form>
</template>
And call it like this:
<Form :cancelEventPassed="true" @cancel="handle_cancel" />
either
<Form/>
Is it possible to achieve this without using any additional property like cancelEventPassed
?
Upvotes: 44
Views: 30908
Reputation: 86
After struggling for quite a long time and reading several comments it finally works in Vue3 (3.2.41) with TypeScript. I should implement it like this:
<template>
<form>
<button v-if="cancelEventPassed" @click="$emit('cancel')">Cancel</button>
</form>
</template>
<script setup lang="ts">
import { getCurrentInstance, ref } from "vue"
const emit = defineEmits(['cancel'])
const cancelEventPassed = ref(!!getCurrentInstance()?.vnode.props?.onCancel)
</script>
And call it like this:
<Form @cancel="handle_cancel" />
Upvotes: 0
Reputation: 1311
Thanks answer from @abdul-aziz-al-basyir .
In Vue 3, use attrs
to replace emit
and listeners
<script setup>
const attrs = useAttrs()
const cancelEventPassed = computed(() => {
return !!attrs.onCancel
})
function toCancel() {
attrs.onCancel()
}
</script>
<template>
<form>
<button v-if="cancelEventPassed" @click="toCancel">Cancel</button>
</form>
</template>
<Form @cancel="handle_cancel" />
Upvotes: -2
Reputation: 1246
const thisInstance = getCurrentInstance();
const hasTaskClickedListener = computed(() => {
if (!thisInstance) return null;
return thisInstance.attrs?.onClicked;
});
explainations:
this
is same like getCurrentInstance()
in vue 3onClick
, onDelete
and so ongetCurrentInstance()
always on setup script unless you will got always null (the value when vue can't see their instance)Upvotes: 1
Reputation: 846
import {useListeners, computed} from 'vue';
const listeners = useListeners();
const hasCancelListener= computed(() => {
return typeof listeners['cancel'] !== 'undefined';
});
Upvotes: 0
Reputation: 2986
Since @prerak-sola's solution doesn't work if you're defining emits like this (as noted by Adam Reis):
const emit = defineEmits<{
(e: 'click', v: MouseEvent): void;
(e: 'update:modelValue', v: MouseEvent): void;
}>();
I figured out, since vue converts all props to an object and just adds a prefix on
before each event-name, you can just check if the property (the event listener) is defined in the vnode
:
const hasClickEventListener = computed(() => !!getCurrentInstance()?.vnode.props?.onClick);
const hasModelValueUpdateListener = computed(() => !!getCurrentInstance()?.vnode.props?.['onUpdate:modelValue']);
I, however, couldn't find anything about this in the official documentation (and it's much more difficult than using useAttrs
). So use with caution.
Upvotes: 13
Reputation: 1246
this.$attrs.onCancel
Vue 3 has removed the $listeners
object instead listener will be parted from the $attrs
object with the on..
prefix.
https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html#overview
Upvotes: 3
Reputation: 10009
In Vue 3, the $listeners
object has been removed. The listeners are now part of the $attrs
object and are prefixed with on
.
In order to check if a particular listener is present or not in a child component, you can do:
computed: {
hasCancelListener() : boolean {
return (this.$attrs && this.$attrs.onCancel) as boolean
}
}
The child component is called as:
<custom-form @cancel="onCancel"></custom-form>
Upvotes: 17
Reputation: 13434
If you're here looking for Vue 3 script setup
or setup function
solution, you can check the attrs
key in getCurrentInstance
function
<template>
<form>
<button @click="$emit('cancel')">Cancel</button>
</form>
</template>
<custom-form @cancel="onCancel"></custom-form>
onMounted(() => {
const instance = getCurrentInstance() // only available inside lifecycle hooks
console.log(instance?.attrs?.onCancel)
})
Upvotes: 0
Reputation: 371
You can check if listener exists like that:
this._events['listener-name']
Upvotes: 0
Reputation: 82459
When there are listeners attached to a component they are available in the $listeners
property of the component.
You can use that property to determine if a specific listener is available. For example, here is a computed property that checks for the existence of a cancel
listener.
computed:{
hasCancelListener(){
return this.$listeners && this.$listeners.cancel
}
}
And here is an example of that used in a component.
console.clear()
Vue.component("CustomForm", {
template:`
<div>
<h1>Custom Form</h1>
<button v-if="hasCancelListener" @click="$emit('cancel')">I have a listener!</button>
</div>
`,
computed:{
hasCancelListener(){
return this.$listeners && this.$listeners.cancel
}
},
})
new Vue({
el: "#app",
methods:{
onCancel(){
alert('canceled')
}
}
})
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
<custom-form @cancel="onCancel"></custom-form>
<hr>
<custom-form></custom-form>
</div>
Upvotes: 62