Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support scoped custom element registries #44110

Open
jpzwarte opened this issue Nov 8, 2021 · 9 comments
Open

Support scoped custom element registries #44110

jpzwarte opened this issue Nov 8, 2021 · 9 comments
Labels
area: elements Issues related to Angular Elements feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature
Milestone

Comments

@jpzwarte
Copy link

jpzwarte commented Nov 8, 2021

Which @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?

@jpzwarte
Copy link
Author

jpzwarte commented Nov 8, 2021

Related to #35328 in having the ability to customise the way how a custom element is created (overwriting renderer.createElement).

@gkalpak gkalpak added the area: elements Issues related to Angular Elements label Nov 8, 2021
@ngbot ngbot bot added this to the needsTriage milestone Nov 8, 2021
@jpzwarte
Copy link
Author

jpzwarte commented Nov 9, 2021

I've got this PoC working atm:

  • Create a custom <app-root> web component using lit (not using @angular/elements)
  • With a custom Renderer3 that uses shadowRoot.createElement instead of document.createElement

This requires the @webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min.js polyfill.

Screenshot 2021-11-09 at 13 02 25

@gkalpak gkalpak added the feature Issue that requests a new feature label Nov 16, 2021
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Nov 16, 2021
@gkalpak gkalpak added the feature: votes required Feature request which is currently still in the voting phase label Nov 16, 2021
@gkalpak
Copy link
Member

gkalpak commented Nov 16, 2021

Thx for the detailed write-up, @jpzwarte 👍
We generally avoid adding support for features that rely on proposed APIs (i.e. that are not part of a stable spec yet), because that can put us in an awkward position if the proposal changes/is rejected.

But let's give it some time to let others chime in.

@angular-robot
Copy link
Contributor

angular-robot bot commented Nov 19, 2021

This feature request is now candidate for our backlog! In the next phase, the community has 60 days to upvote. If the request receives more than 20 upvotes, we'll move it to our consideration list.

You can find more details about the feature request process in our documentation.

@sijakret
Copy link

practical support for web components is also related and discussed here #12045

@angular-robot angular-robot bot added feature: under consideration Feature request for which voting has completed and the request is now under consideration and removed feature: votes required Feature request which is currently still in the voting phase labels Nov 21, 2021
@jpzwarte
Copy link
Author

@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:

  • Creates a new CustomElementRegistry for every component with ShadowDom encapsulation
  • Uses element.getRootNode().createElement(...) instead of document.createElement(...)
  • Added a custom @ElementDefinitions decorator for defining any web components that are used in a component
  • When creating the renderer, check if the component type has any elementDefinitions and register them with the closest registry (either document or the nearest shadowRoot)

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.

@jpzwarte
Copy link
Author

jpzwarte commented May 6, 2023

Chrome has started implementing the spec: WICG/webcomponents#716 (comment)

To reiterate: if you're creating microfrontends using Angular and your design system uses web components, then Angular support for this spec is vital. Perhaps now is the time to start thinking how to support this in Angular? cc @mgechev

@sijakret
Copy link

sijakret commented May 6, 2023

Chrome has started implementing the spec: WICG/webcomponents#716 (comment)

To reiterate: if you're creating microfrontends using Angular and your design system uses web components, then Angular support for this spec is vital. Perhaps now is the time to start thinking how to support this in Angular? cc @mgechev
<3

Please make sure you (and everyone you know who is also plagued by it) up vote this issue as well: #12045

@danwulff
Copy link

Note: you can now test out the Scoped Custom Element Registry feature by turning on the "Experimental Web Platform features" in Chrome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: elements Issues related to Angular Elements feature: under consideration Feature request for which voting has completed and the request is now under consideration feature Issue that requests a new feature
Projects
None yet
Development

No branches or pull requests

4 participants