Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Registered events are missing in component instance #5220

Closed
JohMun opened this issue Jan 7, 2022 · 8 comments
Closed

Registered events are missing in component instance #5220

JohMun opened this issue Jan 7, 2022 · 8 comments
Labels
✨ feature request New feature or request

Comments

@JohMun
Copy link

JohMun commented Jan 7, 2022

Version

3.2.26

Reproduction link

codesandbox.io

(For some reason codesandbox throws the error "Cannot use import statement outside a module" sometimes. Simply reload the page... 🤞)

Steps to reproduce

  1. Create a component and use it in another component
  2. Add an event listener to the child component e.g. @test="() => {}"
  3. In the mounted hook of the child component, add console.log(this)
  4. In this.$attrs onTest exists and we know, the parent listen to the event.
  5. If we add emits: ['test'] in the child component options, onTest is missing in $attrs and we have no chance to detect if the parent uses the event.

What is expected?

I would expect a property called this.$events containing all registered events. In vue 2 it was possible to access registered events with this._events['name']

What is actually happening?

If an event ist added to the emits option of a component, there is no way to detect if the parent listen to the event or not.

@LinusBorg LinusBorg added the ✨ feature request New feature or request label Jan 7, 2022
@LinusBorg
Copy link
Member

LinusBorg commented Jan 7, 2022

For your specific use case, you can just switch from declaring an event to a prop:

props: ['onClose'],
// instead of:
emits: ['close'],

Event Listeners are passed as props with an on prefix, so these two declarations both represent an event. Meaning, if you have an onClose prop, calling emit('close') will attempt to call this prop, presuming it is a function.

So you can now check props.onClose to check wether or not the parent has provided a listener.

I'd still like to keep this issue open as I think we still need something like the suggested option for better type-safe Fallthrough behavior:

<script setup>
import Child from './Child.vue'

const props = defineProps(Child.props)
const emit = defineEmits(child.emits)
// we would need something like this to be able to pass registered listeners transparently:
const listeners = useListeners()
</script>
<template>
  <Child v-bind="{ ...props, ...listeners }"/>
</template>

If we wouldn't care about IDE intellisense for props/events (and type checking), we could have it simpler:

<script setup>
import Child from './Child.vue'

// when we don't define props or events, everything we need is in `attrs`
// but consumers of this component would not be able to get Intellisense and type checking on props.
const attrs = useAttrs()
</script>
<template>
  <Child v-bind="attrs"/>
</template>

But since many in the community get used to the nice type safety that Volar and vue-tsc provide, we need something typesafe.

@JohMun
Copy link
Author

JohMun commented Jan 7, 2022

Thank you for your quick response!

@sqal
Copy link
Contributor

sqal commented Jan 7, 2022

@JohMun something like this can easily be implemented as a composable function here is an example

@LinusBorg
Copy link
Member

@Squal The fact that you need to reach into the internal component instance makes this a bit brittle. I'd rate this a workaround more than a proper solution and would still be in favor to have something more stable in core.

@posva
Copy link
Member

posva commented Jan 11, 2022

Duplicate of #4736 (comment)

@jaketig
Copy link

jaketig commented Jan 26, 2023

For your specific use case, you can just switch from declaring an event to a prop:

props: ['onClose'],
// instead of:
emits: ['close'],

Event Listeners are passed as props with an on prefix, so these two declarations both represent an event. Meaning, if you have an onClose prop, calling emit('close') will attempt to call this prop, presuming it is a function.

So you can now check props.onClose to check wether or not the parent has provided a listener.

I'd still like to keep this issue open as I think we still need something like the suggested option for better type-safe Fallthrough behavior:

<script setup>
import Child from './Child.vue'

const props = defineProps(Child.props)
const emit = defineEmits(child.emits)
// we would need something like this to be able to pass registered listeners transparently:
const listeners = useListeners()
</script>
<template>
  <Child v-bind="{ ...props, ...listeners }"/>
</template>

If we wouldn't care about IDE intellisense for props/events (and type checking), we could have it simpler:

<script setup>
import Child from './Child.vue'

// when we don't define props or events, everything we need is in `attrs`
// but consumers of this component would not be able to get Intellisense and type checking on props.
const attrs = useAttrs()
</script>
<template>
  <Child v-bind="attrs"/>
</template>

But since many in the community get used to the nice type safety that Volar and vue-tsc provide, we need something typesafe.

@LinusBorg any suggestion how to do this on click events with modifier?

<MyComponent @click:someModifier="doSomething"/>

@Squal's composable works but I agree with you it feels more like a work around

I tried this but it doesn't work

<script setup>
  const props = defineProps({
    'onClick:someModifier': {
      type: Function,
      default: null
    }
  })
</script>

@Autumn-one
Copy link

For your specific use case, you can just switch from declaring an event to a prop:

props: ['onClose'],
// instead of:
emits: ['close'],

Event Listeners are passed as props with an on prefix, so these two declarations both represent an event. Meaning, if you have an onClose prop, calling emit('close') will attempt to call this prop, presuming it is a function.

So you can now check props.onClose to check wether or not the parent has provided a listener.

I'd still like to keep this issue open as I think we still need something like the suggested option for better type-safe Fallthrough behavior:

<script setup>
import Child from './Child.vue'

const props = defineProps(Child.props)
const emit = defineEmits(child.emits)
// we would need something like this to be able to pass registered listeners transparently:
const listeners = useListeners()
</script>
<template>
  <Child v-bind="{ ...props, ...listeners }"/>
</template>

If we wouldn't care about IDE intellisense for props/events (and type checking), we could have it simpler:

<script setup>
import Child from './Child.vue'

// when we don't define props or events, everything we need is in `attrs`
// but consumers of this component would not be able to get Intellisense and type checking on props.
const attrs = useAttrs()
</script>
<template>
  <Child v-bind="attrs"/>
</template>

But since many in the community get used to the nice type safety that Volar and vue-tsc provide, we need something typesafe.

我认为你这个想法是对的,感觉vue的团队在用脚思考这个问题!!!

@roydukkey
Copy link

roydukkey commented Mar 29, 2023

@LinusBorg any suggestion how to do this on click events with modifier?

<MyComponent @click:someModifier="doSomething"/>

@jaketig @LinusBorg Have either of you found a solution for modifiers?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
✨ feature request New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants