Script inside shadow dom not working
Asked Answered
M

1

12

I've created and exported one Angular Element (web component in Angular) as a single script tag (user-poll.js). To use this Angular Element I just need to specify below two lines in the host site:

 <user-poll></user-poll>
 <script src="path/to/user-poll.js"></script>

Working JSBin Example

I've following requirements:

  1. Load multiple Angular Elements on the external site.
  2. Load all Angular Elements dynamically

I've following issues:

  1. Loading multiple components on a site gives Angular conflicts, as each component script is a complete Angular app in itself.
  2. To solve conflict issue, I am loading each component in a separate Shadow Dom component (i.e. adding component tag and component script file link inside shadow dom container) so that each component is sandboxed. But component script inside shadow dom not working.

JSBin example of the dynamically loading component inside the shadow dom

<body>

</body>
    setTimeout(() => {
    loadJS();
}, 2000);
function loadJS() {
    var elm = document.createElement("div");
    elm.attachShadow({mode: "open"});
    elm.shadowRoot.innerHTML = `<user-poll><h2>Custom Web Component - user-poll loaded</h2><script src="https://cdn.rawgit.com/SaurabhLpRocks/795f67f8dc9652e5f119bd6060b58265/raw/d5490627b623f09c84cfecbd3d2bb8e09c743ed4/user-poll.js"><\/\script></user-poll>`;
    document.body.appendChild(elm);
}

So, what is the best way to dynamically load multiple Angular Elements on a site?

Misfire answered 29/6, 2018 at 12:12 Comment(4)
One thing to note is elm.innerHTML is not using the ShadowDOM. For that you'd want to use elm.shadowRoot.innerHTML.Harumscarum
Possible duplicate of Can scripts be inserted with innerHTML?Ox
My Bad! Corrected example to proper ShadowDOM.Misfire
also the content of the loaded script is not designed to work with shadow domPinero
P
17

The fact that the script is not executed is not related to Shadow DOM, but to the way you try to insert the <script> element, that is via a string in innerHTML. In this case the string is not interpreted and the script not executed.

If you want it to work you must insert the <script> via appendChild().

You can do it directly by adding a <script> element created with createElement(), or by using a <template> element.

1. with createElement()

Create a <script> element and append it to its parent:

var up = document.createElement( 'user-poll' )
var script = document.createElement( 'script' )
script.textContent = 'document.write( "executed" )'
up.appendChild( script )
elm.attachShadow( { mode: 'open' } )
   .appendChild( up )
<div id="elm"></div>

2. with a <template> tag

Append the content of a <template> element:

elm.attachShadow( { mode: 'open' } )
   .appendChild( tpl.content )
<template id="tpl">
    <user-poll>
        <script>document.write( 'exected' )</script>
    </user-poll>
</template>

<div id="elm"></div>

The script is not executed when the <template> element is parsed, but when its content is appended to the DOM.

PS: anyway I'm not sure it's the best way to load multiple Angular elements: Shadow DOM won't act as a sandbox for Javascript code, as <iframe> does. Instead maybe you'll have to use a module loader.

Pinero answered 29/6, 2018 at 20:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.