2

I developed 2 custom elements in different Angular element projects and when I try to use them in single html I get error "Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry" How to followed this link to develop https://medium.freecodecamp.org/how-to-create-angular-6-custom-elements-web-components-c88814dc6e0a

I know its something related to loading libs twice packaged in both custom elements. How to resolve this?

Thanks

sandeep
  • 996
  • 2
  • 11
  • 22

3 Answers3

5

The Problem: Elements packages up the entirety of Angular and plops it out as a single JS file. When two of these run on a single page, some awful and strange conflicts occur that may or may not affect how your elements behave (more than likely, it will).

The Solution: The only way I have been able to get 2 angular elements to work flawlessly on a single page is to combine them into a single application. They are still separate elements, meaning you can include those custom tags anywhere on the page independently of the other.

You can still follow the steps to build a custom element, but in your app.module.ts file just tack on whatever other elements you want to define:

  ngDoBootstrap() {
    const ele1= createCustomElement(ComponentOne, { injector: this.injector });
    customElements.define('my-ele-one', ele1);

    const ele2= createCustomElement(ComponentTwo, { injector: this.injector });
    customElements.define('my-ele-two', ele2);

    const ele3= createCustomElement(ComponentThree, { injector: this.injector });
    customElements.define('my-ele-three', ele3);

    ....
  }

and your HTML could look something like

<h1> My Elements </h1>

<my-ele-one></my-ele-one>

<p>What a cool element!</p>

<my-ele-two></my-ele-two>

<div class='turtle'>
 <p><my-ele-three></my-ele-three></p>
</div>

This may even cut down your overall page overhead, since now you won't have two full applications being pulled in.

I probably spent 50+ hours trying to configure two separate Angular Elements applications to work together to no avail. I did need to support all browsers which was definitely a huge factor, as I could get certain configurations to work in either Chrome, or IE, but not both. My elements were not simple elements either, they had a lot of working parts (bootstrap modals, ag-grids, etc.). Hope this helps someone in the future.

Afermann
  • 116
  • 1
  • 3
  • Yes this is what I did. Thanks. Another Question to you since you are working on angular element. did you used viewEncapsulation.shadowDom? When I am using this then styles are not getting applied. there is an issue in angular https://github.com/angular/angular/issues/26853 – sandeep Apr 18 '19 at 07:12
  • We had encapsulation turned off (viewEncapsulation.none) because we were having issues with that as well. We were including this in some really old places where our styles didn't really affect the page so it wasn't a big deal for us. Hope you figured it out. – Afermann May 29 '19 at 14:39
  • Being bundled into 1 giant file - are the web-components tree-shakable in the consuming application? (Assuming they use a packaging system like webpack and not plain html script tags) – Charly Jul 23 '22 at 15:09
  • I would commend folks to check out https://hetdev.medium.com/micro-front-end-with-angular-elements-web-components-c56b7a235bcb - Hetzel Cordoba walks through a process that helped me get success with multiple web components rendering in a single html page. – StackOverflowUser Feb 25 '23 at 01:21
4

I found here alternative way which allow me to have two separate angular web-components in another angular project. I do it on MacOs

On your web-commponent (which is separate angular project my-component) run following commands

npm install ngx-build-plus -S
ng update ngx-build-plus --force
ng add ngx-build-plus
ng g ngx-build-plus:wc-polyfill
ng g ngx-build-plus:externals

(this will create file webpack.externals.js in your project directory) and then in my package.json on key scripts I add/edit key:

"build:my-componente:externals": "ng build --output-hashing none --extra-webpack-config webpack.externals.js --prod --single-bundle true --project my-component && cat dist/my-component/polyfill-webcomp-es5.js dist/my-component/polyfills-es5.js dist/my-component/scripts.js dist/my-component/main-es5.js > dist/myComponent.js"

and now I build web component by:

npm run build:my-componente:externals

this will create file dist/myComponent.js - in same way you build myComponent2.js - and in your angular project you can add both to your index.html by

<head>
...
  <script src="./assets/web-components/myComponent.js" type="text/javascript"></script>
  <script src="./assets/web-components/myComponent2.js" type="text/javascript"></script>
...
</head>

and both should work at the same time

Kamil Kiełczewski
  • 85,173
  • 29
  • 368
  • 345
1

I had the same issue but found a slightly different solution that results in a bit less code, especially if you have more than 2 custom elements. I wish I wrote it, but I credit Jeff Delany from fireship.io with this solution.

export class AppModule {
  constructor(private injector: Injector) {}

ngDoBootstrap() {
      const elements: any[] = [
        [AwesomeComponent, 'custom-awesome'],
        [CoolComponent, 'custom-cool']
      ];

      for (const [component, name] of elements) {
        const htmlElement = createCustomElement(
          component, {injector: this.injector});
        customElements.define(name, htmlElement);
      }
    }
}

The index.html could look something like this:

<h1>Multiple Custom Elements</h1>

<div><custom-awesome></custom-awesome></div>
<div><custom-cool></custom-cool></div>

<p>Hope this helps you out</p>
cinerama
  • 47
  • 9
  • I am using angular 11 and this solution has worked also for me. Registering the web-components into the ngDoBootstrap method has displayed all my web-components in the same page successfully. – Yahima Duarte Apr 08 '21 at 13:48