ionic-team / stencil

A toolchain for building scalable, enterprise-ready component systems on top of TypeScript and Web Component standards. Stencil components can be distributed natively to React, Angular, Vue, and traditional web developers from a single, framework-agnostic codebase.
https://stenciljs.com
Other
12.6k stars 791 forks source link

Components get duplicated when parent gets cloned #2197

Closed Gbuomprisco closed 5 months ago

Gbuomprisco commented 4 years ago

Stencil version:

 @stencil/core@1.8.8

I'm submitting a:

[ x ] bug report [ ] feature request [ ] support request => Please do not submit support requests here, use one of these channels: https://stencil-worldwide.herokuapp.com/ or https://forum.ionicframework.com/

Current behavior:

This bug is related to cloning an element, which was already discussed and fixed in this ticket (https://github.com/ionic-team/stencil/issues/1070).

This happens when using Drag&Drop within an Angular application, but I can reproduce easily by cloning the parent using cloneNode(true). It only happens with emulated scoping, not with native Shadow Dom. But I can't use the latter to support Safari.

I don't know the inner workings of Stencil, so apologies in advance if I'm reporting something obvious.

I can see two things happening:

I also added "cloneNodeFix: true" to my Stencil configuration.

Here is an example if I clone the parent element:

Screenshot 2020-02-14 at 13 10 29

Expected behavior:

I would assume the component would not be reinitialized and that the slots would render in the correct order

Steps to reproduce:

Here's an image from my repository that reproduces the bug: stencil-bug

Related code: I created a repo here for reproduction: https://github.com/Gbuomprisco/angular-dd-stencil-bug. It's an Angular project that implements D&D with Material and a Stencil component within.

The stencil project is under /src. Simply run npm run start.

Thanks to the team for the project and the hard work!

Other information:

juliovedovatto commented 4 years ago

Facing similar issue, but with another 3rd-lib that changed DOM.

I tried to force re-render without success. Stencil hard caches DOM or something.

Gbuomprisco commented 4 years ago

Hi @juliovedovatto, yours is probably a separate issues.

You may want to check these out:

voltidev commented 4 years ago

I have the same issue when I clone a parent node to perform transition out animation. A Stencil component gets duplicated inside of that clone. The cloneNodeFix option doesn't work for me.

nilssonja commented 4 years ago

Just noticed the same thing today when an Angular consumer of our Stencil component library tried using Angular Material's Drag&Drop.

My overall assumption/explanation of the problem is that Drag&Drop is effectively duplicating the HTML structure to create a "preview" element that follows the mouse, as well as a "placeholder" element within the list. Since this is effectively creating new nodes in the DOM, the Stencil components contained in the newly created "preview" and "placeholder" elements are freshly initialized with the fully rendered contents of the Stencil components already passed in from the get go. Those contents effectively are just being rendered within the host element as-is, as if they were being slotted in/treated as children, alongside the newly generated nodes created by the Stencil component in it's first render.

fredyagomez commented 2 years ago

Any update on this issue? cloneNode seems to be a native function that should work 100% and have ton of implementations.

oncode commented 1 year ago

I had the problem when using a drag and drop library & using nested Stencil elements with and without Shadow DOM. I'm using a custom cloneNode function now to solve the issue. It clones the elements recursively and when it's a Stencil element without Shadow DOM, it only clones from the slotted elements downwards.

const nativeCloneNodeFn = Node.prototype.cloneNode;

function cloneNode(node: Node, deep: boolean) {
  const clonedNode = nativeCloneNodeFn.call(node, false);
  const srcChildNodes = childNodes(node);

  if (deep) {
    for (let i = 0; i < srcChildNodes.length; i++) {
      if (srcChildNodes[i].nodeType !== 2) {
        const childClone = cloneNode(srcChildNodes[i], true);
        clonedNode.appendChild(childClone);
      }
    }
  }

  return clonedNode;
}

function children(node: Node) {
  return childNodes(node).map((n: any) => n.nodeType === 1);
}

function childNodes(node: Node) {
  const childNodes = node.childNodes;

  // check if element is stencil element without shadow dom
  // and then detect elements that were slotted into the element
  if (node["s-sc"]) {
    const result = [];

    for (let i = 0; i < childNodes.length; i++) {
      const slot = childNodes[i]["s-nr"];

      if (slot) {
        result.push(slot);
      }
    }

    return result;
  }

  return Array.from(childNodes);
}
siamakmontazer commented 1 year ago

I have the same problem when I use Syncfusion components with drag & drop functionalities, any fix planned?

iraklisg commented 1 year ago

same problem here. Seems It was solved in previous versions but reverted back

zovorap commented 1 year ago

Any updates on this? Upgrading to v4.2.1 didn't help. Almost 4 years!

oncode commented 1 year ago

Hi @zovorap Try enabling: https://stenciljs.com/docs/config-extras#experimentalslotfixes

zovorap commented 1 year ago

Thanks a lot @oncode. This solved the issue for me! It was a big deal, since in some cases we can't use shadow: true in our components.

christian-bromann commented 5 months ago

Thanks for verifying @zovorap , closing the issue!