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

Vue 3's v-on syntax does not support listening to custom events with capital letters #5401

Closed
rictic opened this issue Feb 10, 2022 · 13 comments

Comments

@rictic
Copy link

rictic commented Feb 10, 2022

Version

3.2.30

Reproduction link

sfc.vuejs.org/

Steps to reproduce

Notice that the event named 'lower' is being handled by the Vue component, but the event named 'Upper' is not

What is expected?

Event names are an open set, and can include capital letters

What is actually happening?

The event named with a capital letter is not handled


This issue seems to also occur with capital letters in other parts of the event name too

I am a rank Vue novice, and so I may be making a simple error or oversight.

This came up when updating the web site custom-elements-everywhere.com which tracks library and framework support for custom elements. See webcomponents/custom-elements-everywhere#1388

@ygj6 ygj6 added 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. and removed 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Feb 11, 2022
@lidlanca
Copy link
Contributor

 <fires-events @Upper=inc1 @lower=inc2>

compiles to indistinguishable prop name
onUpper
onLower

this is unlikely to change.

to work around this limitation, either use a directive or a template ref to add an event listener directly to the dom element.

@LinusBorg LinusBorg added the wontfix This will not be worked on label Feb 11, 2022
@3zzy
Copy link

3zzy commented Feb 16, 2022

@LinusBorg If that's the case then the Vue doesn't get a perfect 100% as mentioned in the docs so the docs need to be updated to reflect that.

@LinusBorg
Copy link
Member

LinusBorg commented Feb 16, 2022

Sure, PRs are welcome.

However, consider that we don't prevent you from listening to this event - you can add the event listener with a small custom directive, as mentioned - it's just not supported via v-on as it collides with Vue's own event name semantics.

@rictic
Copy link
Author

rictic commented Feb 16, 2022

Sent over a docs PR, but if there's an ergonomic to support arbitrary event names, we could instead update the custom-elements-everywhere test for this feature: https://github.com/webcomponents/custom-elements-everywhere/blob/221ea4744373f34112331949c0d85d4b45e41d69/libraries/vue/src/components.js#L157-L163

@LinusBorg
Copy link
Member

LinusBorg commented Feb 16, 2022

@rictic thanks!

So here's the recommendation:

In Vue, one can define custom directives (like that internal ones, i.e. v-on) to wrap imperative DOM operations in a declarative way. These custom directives can be declared locally in each component or registered globally in an app to be available in all child components.

What we would want here is a basic way to add and remove event listeners while preserving the event name casing at all times. This can be achieved with a few lines of code. here's a demo with local registration: SFC Playground

Registering this directive globally (in one app's context) would look like this:

// v-event:arg"value"
app.directive('event', {
    beforeMount(el, { arg, value }) {
      el.addEventListener(arg, value)
    },
    beforeUnmount(el, { arg, value }) {
      el.removeEventListener(arg, value)
    }
  })

Would that be simple enough to be considered as a declarative solution for these tests?

@rictic rictic changed the title Vue 3 does not support listening to custom events with capital letters Vue 3's v-on syntax does not support listening to custom events with capital letters Feb 16, 2022
@rictic
Copy link
Author

rictic commented Feb 17, 2022

That works well.

Given its small size, what do you think about adding the event directive into Vue by default, just so that it's a pattern that components can rely on existing, and that is more likely to be findable?

@LinusBorg
Copy link
Member

LinusBorg commented Feb 17, 2022

@rictic That's indeed something to consider. However I'm worrried that it's similarity to v-on would make it an easy point of confusion, and given that it's only real purpose is to work around a limitation in the handling of custom events, I'd be a bit hesitant to make it a new API on the same level as v-on. However that's just my gut feeling for now.

At the minimum, we should document the limitation and this pattern as a recommended solution in your section about "Vue & Custom Elements" here.

@nileshtrivedi
Copy link

The Vue docs still say that "Vue scores a perfect 100% in the Custom Elements Everywhere tests" but the tests actually show 91% score:

image

@aztalbot
Copy link
Contributor

aztalbot commented Aug 2, 2022

@LinusBorg I’ve seen this become a point of friction in migrating from vue2 to vue3 if the app uses a lot of custom elements - you end up with either less idiomatic Vue code throughout by using custom directives for very common event handling, or waiting on publishers of the web components you use to update their library.

I find it surprising as a user that you can’t simply consume a web component and listen for the exact event you want - I know this leads to confusion for others, too. The pattern Vue already follows for props with the .prop modifier seems like it would make sense here; that prefixes the prop with . to indicate different treatment. That helps with setting properties on Web Components, and I’d expect a similar feature for CustomEvents, like v-on:fooBar.custom to indicate that you are trying to listen for this exact CustomEvent. I’d anticipate the resulting prop after transformation could be on.fooBar, so that existing case transformations don’t capitalize it, and in patchEvent you can check for . to know to use the following event name unchanged. Or it can be a different distinguishing character (like :).

Happy to open an RFC for this if you think it has any potential - I do think this feels like a gap that Vue itself should handle somehow.

@lidlanca
Copy link
Contributor

lidlanca commented Aug 2, 2022

related RFC discussion
vuejs/rfcs#451

@skirtles-code
Copy link
Contributor

skirtles-code commented Oct 18, 2022

I believe this was fixed in 3.2.38 via 0739f89.

Update: Running the original reproduction against the latest Vue still shows the reported problem. However, I believe that's because isCustomElement is not set correctly, which is also why it shows a warning. I'm not aware of any way to change that setting on the SFC Playground, so I've created a reproduction here: https://jsfiddle.net/skirtle/4ery6m1z/. I believe that shows that the original problem is fixed.

You may notice that it works even without the isCustomElement. However, if you move the customElements.define call to the bottom (rather than having it at the top) then it behaves the same as the SFC Playground example. In that case, isCustomElement does fix the problem. Here's the broken version, for comparison: https://jsfiddle.net/skirtle/y4z2n0w8/.

@LinusBorg LinusBorg removed the wontfix This will not be worked on label Oct 19, 2022
@LinusBorg
Copy link
Member

Just checked this and yes, the changed mentioned by @Skirtle addresses this issue, though only the related issues in the docs repo were mentjoned in the commit message as this one was already closed at that point.

So this issue is now solved.

@edimitchel
Copy link

As for now, the website https://custom-elements-everywhere.com is still showing Vue as 91% web component compliant.

I raise a PR to solve that: webcomponents/custom-elements-everywhere#2074

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

No branches or pull requests

9 participants