Closed east-rain closed 5 months ago
Interesting use case, I'll try to work on a solution. but, just to be clear, you attempt to create vue3 web component and then use it in vue2 project?
Yes, I use vue3's component in vue2 project and finally I found the way.
In render, I receive inner component's event, and emit again.
But I have a other problem. With this way, I can't receive slot.
The Project that use Web Component, pass a slot But in Web Component can't receive that slot content.
If I use a plain defineCustomElemt lib in Vue, I can get a slot content. Do you have any good solution?
I am not sure how did you solve the issue, but it would be nice if we could add this feature to the lib, maybe you have time to open a PR
Regarding the slots, I need to check, can you give a more detailed example of the slot use case?
my-component.vue has slot
<template>
<div>some text</div>
<slot/>
</template>
and this component define web component as your solution.
return VueDefineCustomElement({
styles: [ cssFrameworkStyles ],
props: rootComponent.props,
...........
return () => h(rootComponent, .......) // rootComponent is <my-component>
And I use this web component this way.
<my-component><div>hi</div><div slot="default">hi</div><template #default>hi</template></my-component>
But my browser can't render as a slot. This is a same problem: https://github.com/EranGrin/vue-web-component-wrapper/issues/10
Also I can't use web component's method. Do you have a solution?
I have found a solution for
slots & named slots
I've already spent several hours over here, and I need to divert my attention back to none open-source project 😉
It will take a few days for me to update the docs and create code example
Also I can't use web component's method. Do you have a solution?
Please elaborate
New version is up with several new features. https://www.npmjs.com/package/vue-web-component-wrapper
It looks nice. Do you have a case using provide?
This is a situation. A component provide('val', computedObject); B component inject('val')
When using a B component, dev console says '[Vue warn]: injection "val" not found.'
First time, I try to provide globally in pluginsWrapper. But, A component's computedObject is computed(). It is created when A component created.
So It's impossible to provide globally in pluginsWrapper. Do you have good idea?
Hi There, In case you did not notice, in the npm readme page of the plugin there are links to webpack and vite implementations here is vite https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md&startScript=vite-demo
Furthermore, I might have some suggestions for how to solve your problem, but I would need to see it more clearly Please create stackblitz demo demonstrating your issue
I'm sorry. it's little hard to implement. I try to explain detail. And if you steel confused, I will make a stackblitz.
component A ( wrapper component )
createWebComponent({
rootComponent: {
template: "<template><div><slot/><div></template>",
setup(props, {}) {
provide('myInfo', computedSomeObject);
}
},
elementName: 'foo-wrapper-component',
plugins: pluginsWrapper,
cssFrameworkStyles: style,
VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
});
component B
createWebComponent({
rootComponent: {
template: "<template></template>",
setup(props, {}) {
const val = inject('myInfo')
}
},
elementName: 'foo-component',
plugins: pluginsWrapper,
cssFrameworkStyles: style,
VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
});
And I use this web component like this.
<template>
<foo-wrapper-component>
<foo-component/>
<foo-component/>
<foo-component/>
</foo-wrapper-component>
</template>
When load this page, [Vue warn]: injection "myInfo" not found. Because foo-wrapper-component and foo-component have own instance area. So foo component can't see foo-wrapper's "provide".
But when use this component as a normal vue component, Everything is OK.
Do you have a good idea? sharing a value each web component? (If there is no solution, I can get 'myInfo' from foo-wrapper's refs, and pass this object to foo-component's prop. But I'd like know sharing a data Each web component)
I found out how to add support for the provide/inject, it might take few days for me to release a new version
Wow. Could you share a your idea?
You can check the latest release, 1.4.0 🎉
I check your idea. But it can works in option API component. Can't access to Composition API's provide.
ok, I'll check it in the coming days
The provide function of the composition does have an issue as it only fire after the plugin run, which prevents the inject to get the value of the providers, the inject in the composition does work normal. I did found a work around
<template>
<div>
<h1 class="text-xl">
{{ testInject }}
</h1>
</div>
</template>
<script lang="ts">
import { ref, onMounted, defineComponent, inject } from 'vue';
export default defineComponent({
name: 'AppChild',
emits: ['customEventTest', 'update:modelValue'],
props: {
baseUri: { type: String, default: 'test.me' },
modelValue: { type: String },
apiToken: { type: String, required: true },
},
setup(props, { emit }) {
// Reactive properties
const configuration = ref({});
const testInject = ref('');
// Inject
const testKey = inject('testKey');
// Mounted equivalent
onMounted(() => {
console.log(testKey); // Outputs: 'This is test data'
testInject.value = testKey as string;
});
// Methods
const testEmit = () => {
emit('customEventTest', { testEvent: '123456789' });
};
return {
configuration,
testInject,
testEmit
};
},
});
</script>
<style>
.test-heading {
font-size: 2em;
margin-top: 2rem;
}
a {
@apply text-gray-900 hover:text-gray-700;
}
header {
@apply font-sans;
}
main {
@apply font-sans;
}
</style>
<template>
<div>
<header class="bg-blue-300 rounded-lg shadow-lg p-4 mx-0 md:mx-20">
<nav>
<ul class="flex justify-between sm:mx-1 md:mx-20">
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<router-link to="/test1">Test route 1</router-link>
</li>
<li>
<router-link to="/test2">Test route 2</router-link>
</li>
<li>
<router-link to="/test3">Test route 3</router-link>
</li>
</ul>
</nav>
</header>
<header class="bg-gray-800 text-white text-center p-4 mt-4 rounded-lg shadow-lg mx-0 md:mx-20">
<div style="display: flex; justify-content: space-between;">
<div class="text-black flex">
<input
class="p-2 rounded-lg shadow-lg"
/>
<div class="text-white ml-4 align self-center">
{{ modelValue }}
</div>
</div>
<button
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
@click="testEmit"
>
emit event outside
</button>
</div>
</header>
<main class="mt-4 p-4 mx-0 md:mx-20 bg-gray-200 rounded-lg shadow-lg">
<router-view />
</main>
<footer class="bg-gray-800 text-white text-center p-4 mt-4 rounded-lg shadow-lg mx-0 md:mx-20">
<div>
<input v-model="reactiveProperty" placeholder="Type here...">
<p>Value: {{ reactiveProperty }}</p>
</div>
<div class="mb-2">
<slot></slot>
</div>
<div class="flex justify-center">
<slot name="customName"></slot>
</div>
</footer>
</div>
</template>
<script setup lang="ts">
import { ref, provide, getCurrentInstance, getCurrentScope, ComponentInternalInstance } from 'vue';
// Define props
const props = defineProps({
apiToken: String,
baseUri: { type: String, default: 'test.me' },
modelValue: String
});
// Define emits
const emit = defineEmits(['customEventTest', 'update:modelValue']);
const reactiveProperty = ref('initial Value');
// Reactive properties
const configuration = ref({});
const credentials = ref({
apiToken: props.apiToken,
baseUri: props.baseUri
});
// Provide
interface InstWithProvides extends ComponentInternalInstance {
provides: Record<string, any>;
}
const inst = getCurrentInstance() as InstWithProvides;
if (inst?.provides) {
inst.provides['testKey'] = reactiveProperty;
}
console.log('inst', inst);
// Provide multiple values
console.log(provide); // Outputs: 'I am provide/inject data via default slot'
// Method
const testEmit = () => {
emit('customEventTest', { testEvent: '123456789' });
};
console.log('getCurrentInstance', getCurrentInstance());
console.log('getCurrentScope', getCurrentScope());
// Watchers or computed properties (if any) can be added here
</script>
<style>
.test-heading {
font-size: 2em;
margin-top: 2rem;
}
a {
@apply text-gray-900 hover:text-gray-700;
}
header {
@apply font-sans;
}
main {
@apply font-sans;
}
</style>
I think your idea is amazing. But there is one issue. My goal is to use vue3 components in a vue2 project.
when i use your way, then i can't receive custom event. Below is a component that can receive text input.
The code below uses the component created above. It is made with vue2.
when i input some text, then onInput() never triggering
But
i create web component this way,
when i input some text, then onInput() event trigger and i can check this event is CustomEvent()
is there a way to receive events emitted from a component created through a wrapper?