Support scoped custom element registries
See original GitHub issueWhich @angular/* package(s) are relevant/releated to the feature request?
compiler, core, elements
Description
When using microfrontends, you can have the following setup:
- app-shell
- microfrontend A (implemented using lit.dev + web component design system)
- microfrontend B (implemented using angular elements + web component design system)
So the app shell dynamically loads a microfrontend javascript bundle and instantiates the web component and appends it to the DOM in the correct location.
Microfrontend B is implemented using angular elements so the entire angular app is wrapped in a custom element and it won’t conflict with any other microfrontends built using Angular (possibly with a different angular version)
Now the problem arises when A & B use different versions of the design system npm package. Say you have <x-button>
versions 1 and 2 in the design system. The first one who registers the custom element via window.customElements.define('x-button', Button)
“wins”. The second one who attempts that gets an exception.
Even if you detect if x-button
is already defined, you still have issues because A & B expect a specific version of the custom element.
Proposed solution
There is a proposal to handle this, written by @justinfagnani https://github.com/WICG/webcomponents/blob/gh-pages/proposals/Scoped-Custom-Element-Registries.md
This basically comes down to custom elements having a “local” CustomElementRegistry
and children of that customElement using that local version instead of the global window.customElements
one.
I would propose @angular/elements
having an option to use the above proposal. And then having any web component children not using document.createElement
, but somehow using a local CustomElementRegistry
to instantiate the elements.
In plain web components, every shadowRoot basically “inherits” the local CustomElementRegistry
. In Angular however you can have a mix of angular & web components. So not every web components has a parent web component. So you would probably have some sort of CustomElementRegistry
injectable that Ivy/Renderer3 then uses to create the element instead of document.createElement
.
Alternatives considered
The only workaround I see so far is to use an <iframe>
to host the angular microfrontend. That way all web components are isolated to the iframe.
I would love the ability to handle this myself, but I don’t see how you could change Ivy/Renderer3 to not use document.createElement
to instead use an injected local CustomElementRegistry
?
Issue Analytics
- State:
- Created 2 years ago
- Reactions:26
- Comments:6 (2 by maintainers)
Top GitHub Comments
@gkalpak So this isn’t really hard to do as it turns out; I have it working by providing my own
RendererFactory2
implementation.Some highlights:
CustomElementRegistry
for every component withShadowDom
encapsulationelement.getRootNode().createElement(...)
instead ofdocument.createElement(...)
@ElementDefinitions
decorator for defining any web components that are used in a componentelementDefinitions
and register them with the closest registry (eitherdocument
or the nearestshadowRoot
)See https://gist.github.com/jpzwarte/6dacea6a51a4e7afca9b80014b376e3f for more info
My guess is that it would be pretty easy to add this to Angular in such as way that it would be non-breaking for people not using it.
practical support for web components is also related and discussed here https://github.com/angular/angular/issues/12045