2
votes

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

3

3 Answers

3
votes

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.

3
votes

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

0
votes

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>