karol-f / vue-custom-element

Vue Custom Element - Web Components' Custom Elements for Vue.js
https://karol-f.github.io/vue-custom-element/
MIT License
1.97k stars 187 forks source link

Nested custom elements gets recreated. #157

Closed KosmoKeLi closed 5 years ago

KosmoKeLi commented 5 years ago

If I nest custom elements it get re-created.

<custom-element>
  <custom-element>
    <custom-list :items.prop="items"></custom-list>
  </custom-element>
</custom-element>
items = [{ val1: 'abc', val2: 'def' }];

The custom element <custom-list> gets re-created twice. (mounted() gets called twice) This causes it to drop bound property values.

If I bind an object to property value on <custom-list> and output the current set value to the console in mounted(), the first output contains the bound value but then it's undefined. It looses it's bound values when it's recreated.

karol-f commented 5 years ago

Hi, check if you can re-order Vue.customElement('custom-list', ...) definition. Try it e.g. after Vue.customElement('custom-element', ...) definition. Hope it will help.

Another way (not perfect) is to use <custom-list> inside Vue template of <custom-element>.

KosmoKeLi commented 5 years ago

Hi. I tried reordering the two custom elements one way or the other, but the problem is still there.

Also, it's impossible if the order is important, since I have a hundred components that could fit in any combination.

Also I should mention that I use .vue files for every component and the custom elements are nested inside another .vue file's template.

karol-f commented 5 years ago

Can You please prepare CodeSandbox (e.g. fork https://codesandbox.io/s/jv4nvmp74v) with the problem? I will try to fiddle with it. Regards.

KosmoKeLi commented 5 years ago

Ok. Here's the simplest version of the problem: https://codesandbox.io/s/73l7wo0666

If you check the console, you'll see that it mounted hello-world twice. It should only be mounted one time.

Is <slot></slot> the problem here perhaps?

Edit: This works fine in standard Vue using Vue.component('my-container', Container);. All components that I'm using have been translated to use Vue.customElement() instead, problem did not exist before.

karol-f commented 5 years ago

@KosmoKeLi re-ordering definitions worked for me - https://codesandbox.io/s/vj44k93x23

Should I check for other solutions?

KosmoKeLi commented 5 years ago

Yes please. Like I said. I have over a hundred different components and a lot of them have slots and I can't re-order them.

It would be great if the order wasn't a problem.

Also I should mention that I use Typescript and Vue Class Component. I use vue-custom-element like this:

Vue.customElement('my-container', (MyContainer as any).options); to get past the missing _init method or missing template/render method error.

karol-f commented 5 years ago

This behaviour is due to the fact that after You put child/slot Web Component (in Your example it's `

`) into HTML it's detected and initialized by browser. Then parent Web Component grabs already initialized HTML and put it into Vue, which changes it into VDOM and put it's HTML back into a page. Browser than detect it as new Web Component and initialize it. We should try to avoid initializing Web Component on the first time. I have some ideas but didn't have time to look at it.
KosmoKeLi commented 5 years ago

Ok. I did some research and it looks like it's by design.

I'm going to look into another solution.

Thank you so much for analyzing this problem for me.

karol-f commented 5 years ago

@KosmoKeLi Quick and not so beautiful workaround is to use isParentWebComponentReady mixin - https://codesandbox.io/s/p9v211jr7m

You can extend it on your own to e.g. use passed "parent" prop element however it's already working.

Regards.