How to emit events to the outside?
Closed this issue · 11 comments
Hello,
I was wondering how to emit the events outside of the webcomponent?
Without this plugin I managed to get it to work and Vue produces CustomEvent, to which I can subscribe with addEventListener
.
What would be the solution to use it?
My understanding is that the createWebComponent
gets rootComponent
and elementName
which are binding the event emitting on one and I am listening on another element.
Thanks
Hi There,
This is indeed a very interesting use case, I haven't tried or tested the custom event on with the web component, but the structure should support it.
With that being said, I might need to add minor changes in case it doesn't work.
For your ref
- https://vuejs.org/guide/extras/web-components.html#building-custom-elements-with-vue
- https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
Kindly let me know if you manage to make it work
Thanks
Hey,
Sorry for the late reply.
I managed to do it without the plugin as I've decided to code everything on my own. Vue already produces CustomEvent
when you build it as a webcomponent.
But in my opinion, the Vue component that is actually converted into the CustomElement should just emit
to the outside and not be wrapped into anything since emit works only for parent and does not propagate to grandparent.
Thank you for your reply 😃
Hi,
Not quite sure what do you refer to
But in my opinion, the Vue component that is actually converted into the CustomElement should just emit to the outside and not be wrapped into anything since emit works only for parent and does not propagate to grandparent.
But glad to hear you found your desired solution :-)
The plugin "vue-web-component-wrapper" attempts to solve issues of plugin integration and shadow DOM style injection
but if your use case doesn't suffer from these issues, then using vue built in webComponent is obviously a better choice
Hi, I would like to emit events to the outside using with plugin. How can I achieve this?
HI @zAlweNy26, I might need to release another version, which add bubbles: true to the custom element.
I'll let you know when it's ready
Ok thanks @EranGrin, for the moment I found a workaround using the default "defineCustomElement" available in Vue:
const Widget = defineCustomElement(App)
class ExposedWidget extends Widget {
constructor(props?: Record<string, any>) {
super(props)
}
public toggle() {
const inst = (this as any)._instance
if (!inst) throw new Error('Component not mounted')
inst.exposed.toggle()
}
public refresh() {
const inst = (this as any)._instance
if (!inst) throw new Error('Component not mounted')
inst.exposed.refresh()
}
}
customElements.define('chat-widget', ExposedWidget)
And for the styles I just did:
<style>
@import './style.css';
</style>
The events are emitted (without touching anything) but as I wanted to expose some methods too, I added this code. I hope it helps.
This seems to be working just fine
Here is a vue component that will be wrapped into the web-component
import { storeToRefs } from 'pinia'
import { useCounterStore } from '../pinia/counter.store'
const counterStore = useCounterStore()
// Use the standard event dispatching for custom elements
const dispatchEvent = (eventName: string, detail: { count: number; }) => {
const event = new CustomEvent(eventName, {
detail,
})
window.dispatchEvent(event)
}
const { count, name, doubleCount } = storeToRefs(counterStore)
const { increment: originalIncrement, reset } = counterStore
const increment = () => {
originalIncrement()
dispatchEvent('incremented', { count: count.value })
}
</script>
<template>
<div class="flex flex-col justify-center items-center">
<div class="flex flex-col justify-center items-center text-2xl bg-blue-300 rounded-xl p-4">
<h1>I am a route 3 (and Support Pinia!)</h1>
<p>Check localstorage</p>
</div>
<p>{{ name }}</p>
<p>Count: {{ count }}</p>
<p>Doble Count: {{ doubleCount }}</p>
</div>
<div class="flex py-2">
<button class="p-2 bg-blue-300 text-black border-cyan-300 rounded-l-lg rounded-r-none w-full" @click="increment">
Increment
</button>
<button
:class="{ 'bg-slate-300 text-slate-500': count == 0 }"
class="p-2 bg-red-600 text-white border-cyan-300 rounded-r-lg w-full duration-300"
:disabled="count == 0"
@click="reset"
>
Reset
</button>
</div>
</template>
Here is the parent host of the web component
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite 4 + Vue 3.3 + Typescript 5 + Web Component</title>
</head>
<body>
<h1 class="text-test" style="text-align:center;">Vite 4 + Vue 3.3 + Typescript 5 + Web Component</h1>
<pre style="margin-left: 3%;">
<code><my-web-component
lang="de"
route="/route1"
class="my-web-component"
api-token="++++++++++++++++++++++++"
base-uri="https://my.base.uri"
>
</my-web-component>
</code>
</pre>
<my-web-component
lang="de"
route="/route1"
class="my-web-component"
api-token="++++++++++++++++++++++++"
base-uri="https://my.base.uri"
></my-web-component>
<script type="module" src="./src/main.ts"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
window.addEventListener('incremented', function(event) {
console.log('Counter was incremented, current count:', event.detail.count);
// You can also handle the event here as needed
});
});
</script>
</body>
<style>
.text-test {
color: red;
font-size: 2rem;
}
</style>
</html>
Nice example. I would just like to add that Vue already makes a custom event if you emit from root component. Then you would listen to the my-web-component
and not on window
. This event name would have to be really unique not to conflict with anything.
Ok, nice never tried this, can you give an example?
The issue appears to be inactive.
New version is up, with support for event emit
https://www.npmjs.com/package/vue-web-component-wrapper