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

Using multiple Angular Elements, ngDoBootstrap runs twice, breaking with CustomElementRegistry #23732

Closed
METACEO opened this issue May 7, 2018 · 62 comments

Comments

@METACEO
Copy link

commented May 7, 2018

I'm submitting a bug report.

Edit: screenshots of this issue are posted below, here

Using multiple Web Components built from Angular Elements breaks the consuming application.

I have the following three repositories:

  1. example-angular-elments-app - the consuming application
  2. example-angular-elments-component-box - outputs the box WebComponent
  3. example-angular-elments-component-button - outputs the button WebComponent

Both components (2 and 3) build the files found in the consuming app's src/assets/elements folder.

You should receive the same error in your console (or something similar, depending on which component you load dynamically first) after following the reproduction steps further below.

example-angular-elements-component-box.js:4 ngDoBootstrap example-angular-elements-component-box
example-angular-elements-component-box.js:4 ngDoBootstrap example-angular-elements-component-box
example-angular-elements-component-box.js:4 ERROR DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry
    at ze.define (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:1:12582)
    at ze.value (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:33689)
    at ze.define (http://localhost:4200/assets/elements/example-angular-elements-component-button.js:1:12582)
    at ze.value (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:33689)
    at e.ngDoBootstrap (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:4:121355)
    at e._moduleDoBootstrap (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:4:37688)
    at http://localhost:4200/assets/elements/example-angular-elements-component-box.js:4:36842
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:4200/polyfills.js:2704:26)
    at Object.onInvoke (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:4:30158)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:4200/polyfills.js:2703:32)

After including the second Angular Element WebComponent, you'll see a line like example-angular-elements-component-box.js:4 ngDoBootstrap example-angular-elements-component-box logged twice, illustrating that component bootstrapping twice, likely causing the CustomElementRegistry error.

Expected behavior

The consuming application does not break when consuming multiple WebComponents built from Angular Elements.
The ngDoBoostrap method is not called twice, or at least checks for component registration.

Minimal reproduction of the problem with instructions

  1. Clone the example-angular-elments-app Angular 6 project.
  2. Run npm i
  3. Run npm start
  4. Open http://localhost:4200 and open the developer console.
  5. Click on one of the buttons to load a component.
  6. Click on the other button to load the other component.
  7. View the console and see how the component that was loaded first runs its' ngDoBootstrap method again, before breaking the page.

Environment


[xxx example-angular-elments-app]$ npm run ng -- -v

> example-angular-elments-app@0.0.0 ng /home/xxx/WebstormProjects/example-angular-elments-app
> ng "-v"


     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 6.0.0
Node: 8.11.1
OS: linux x64
Angular: 6.0.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, http, language-service, material, platform-browser
... platform-browser-dynamic, router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.6.0
@angular-devkit/build-angular     0.6.0
@angular-devkit/build-optimizer   0.6.0
@angular-devkit/core              0.6.0
@angular-devkit/schematics        0.6.0
@ngtools/webpack                  6.0.0
@schematics/angular               0.6.0
@schematics/update                0.6.0
rxjs                              6.1.0
typescript                        2.7.2
webpack                           4.6.0

Browser:
- [x] Chromium Web Browser (desktop) Version 65.0.3325.181 (Developer Build) Fedora Project (64-bit)
 
For Tooling issues:
- Node version: 5.6.0
- NPM version: 5.6.0
- Platform: Linux
@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 7, 2018

I have similar problems. zone runs twice...

@METACEO

This comment has been minimized.

Copy link
Author

commented May 7, 2018

@BioPhoton - I removed the zone.js from the polyfills to avoid that issue, in both of these example projects:

Which then meant I had to come back and add zone.js into each of the component's index.html file (if I wanted to run them locally, before exporting and consuming them.)

@METACEO

This comment has been minimized.

Copy link
Author

commented May 8, 2018

I'll try and load multiple Angular Elements together within a single page if anyone has any that they've exported, simply to determine if it's an error in my own approach.

Any up-to-date documentation as to how to officially utilize @angular/elements and export a consumable WebComponent would be awesome.

@mhevery mhevery added the comp: core label May 8, 2018

@ngbot ngbot bot added this to the needsTriage milestone May 8, 2018

@mhevery mhevery added comp: elements and removed comp: core labels May 8, 2018

@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 9, 2018

I managed to get multiple components (bundled in one file) running in another ng app. Is this what you're trying to do? Or standalone?

@METACEO

This comment has been minimized.

Copy link
Author

commented May 9, 2018

@BioPhoton - standalone, built individually and independently from one another.

So, for example, if I work with multiple teams then I could download the WebComponent that they built with their own Angular Elements (they're own dependencies, versions, build pipeline, etc.) I am interested in consuming their built artifact.

By appending scripts to my document's head, I should also be able to lazily load these WebComponents. I'm loading two in my example application, the first one works but once the second one is introduced the app breaks - while they are separate files and separate builds, they're stepping on each other once introduced and consumed.

@METACEO

This comment has been minimized.

Copy link
Author

commented May 9, 2018

This is very important to fix, I believe, especially considering the fact that once Angular Element WebComponents are built and published for everyone else to use (Wordpress, Polymer, etc.) they all look to be breaking one another in what appears to be friendly fire.

@METACEO

This comment has been minimized.

Copy link
Author

commented May 9, 2018

@skreborn - I'm reading over some other bug/feature entries and read your post from #20891

@pstrh I would assume that since Angular Elements creates a sandbox for itself and is bound to its host element, you would be able to use different Angular versions just like you could use any other framework for the other elements on your page. Different elements shouldn't need to know what code controls the others.

I know that your post was from January and many things have changed, technically and at a high level, I imagine, but would you have idea as to why my Box WebComponent's module would be running ngDoBootstrap again when my Button WebComponent is introduced?
(Visa-versa, depending on which WebComponent was lazily loaded first.)

@gkalpak

This comment has been minimized.

Copy link
Member

commented May 9, 2018

I wonder if it is because you are including the polyfills in both scripts.
(Despite whether this is causing the error or not, I guess you should only include the polyfills once per app.)

@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 9, 2018

No. I cerated a cli based setup for standalone and angular components.

I excluded all polyfills in the component bundles and included them in my "main" app.

@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 9, 2018

Ill try more tonight. I also will publish my setup on npm and have a cli "story" (docs for setup) nearly done.

@METACEO

This comment has been minimized.

Copy link
Author

commented May 9, 2018

@gkalpak I'll try excluding the plolyfills from both the Box and Button WebComponents, re-build each and then consume both again and see what happens.

@BioPhoton thank you, I'd greatly appreciate comparing our work. What you've done excluding the polyfills goes along with @gkalpak's direction too, so I'll again this and report back.

@METACEO

This comment has been minimized.

Copy link
Author

commented May 10, 2018

@gkalpak and @BioPhoton following both of your suggestions, I commented out the polyfills.ts from the script that concats the built files, like so:

const fs = require('fs-extra');
const concat = require('concat');

(async function build(){

  const files = [
    './dist/scripts.js',
    './dist/runtime.js',
    // './dist/polyfills.js',
    './dist/main.js'
  ];

  await fs.ensureDir('elements');

  await concat(files, 'elements/example-angular-elements-component-button.js');

})();

I did this with both of the example components.

I am still getting the same error. The first component's module is logging to console when the second component is introduced and then breaks.

I will copy and paste my browser's log and include screen shots below.

Angular is running in the development mode. Call enableProdMode() to enable the production mode.
example-angular-elements-component-box.js:3 ngDoBootstrap example-angular-elements-component-box
example-angular-elements-component-box.js:3 ngDoBootstrap example-angular-elements-component-box
example-angular-elements-component-box.js:1 Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry
    at ze.define (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:1:12582)
    at ze.define (http://localhost:4200/assets/elements/example-angular-elements-component-button.js:1:12582)
    at new e (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:121311)
    at http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:74118
    at no (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:74335)
    at http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:84190
    at new e (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:84200)
    at Object.Eo [as createNgModuleRef] (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:83838)
    at t.create (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:115342)
    at http://localhost:4200/assets/elements/example-angular-elements-component-box.js:3:36458
ze.define @ example-angular-elements-component-box.js:1
ze.define @ example-angular-elements-component-button.js:1
e @ example-angular-elements-component-box.js:3
(anonymous) @ example-angular-elements-component-box.js:3
no @ example-angular-elements-component-box.js:3
(anonymous) @ example-angular-elements-component-box.js:3
e @ example-angular-elements-component-box.js:3
Eo @ example-angular-elements-component-box.js:3
t.create @ example-angular-elements-component-box.js:3
(anonymous) @ example-angular-elements-component-box.js:3
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
onInvoke @ example-angular-elements-component-box.js:3
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:387
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
e.run @ example-angular-elements-component-box.js:3
e.bootstrapModuleFactory @ example-angular-elements-component-box.js:3
zUnb @ example-angular-elements-component-box.js:3
p @ example-angular-elements-component-button.js:2
3 @ example-angular-elements-component-box.js:3
p @ example-angular-elements-component-button.js:2
n @ example-angular-elements-component-button.js:2
e @ example-angular-elements-component-button.js:2
(anonymous) @ example-angular-elements-component-button.js:2
(anonymous) @ example-angular-elements-component-button.js:2
example-angular-elements-component-box.js:1 Uncaught TypeError: Illegal constructor
    at t.e.(:4200/anonymous function) (http://localhost:4200/assets/elements/example-angular-elements-component-box.js:1:8552)
    at t [as constructor] (example-angular-elements-component-box.js:3)
    at new t (example-angular-elements-component-box.js:3)
    at HTMLDocument.n.createElement (example-angular-elements-component-box.js:1)
    at HTMLDocument.n.createElement (example-angular-elements-component-button.js:1)
    at app.component.ts:24
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
    at Object.onInvoke (core.js:4071)
    at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:387)
    at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138)
e.(anonymous function) @ example-angular-elements-component-box.js:1
t @ example-angular-elements-component-box.js:3
t @ example-angular-elements-component-box.js:3
n.createElement @ example-angular-elements-component-box.js:1
n.createElement @ example-angular-elements-component-button.js:1
(anonymous) @ app.component.ts:24
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:388
onInvoke @ core.js:4071
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke @ zone.js:387
push../node_modules/zone.js/dist/zone.js.Zone.run @ zone.js:138
(anonymous) @ zone.js:872
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:421
onInvokeTask @ core.js:4062
push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask @ zone.js:420
push../node_modules/zone.js/dist/zone.js.Zone.runTask @ zone.js:188
drainMicroTaskQueue @ zone.js:595
push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask @ zone.js:500
invokeTask @ zone.js:1540
globalZoneAwareCallback @ zone.js:1566
  1. I can add multiple box WebComponents without any issue. See how the box's module logged to the console once, upon introduction into the application.
    screenshot from 2018-05-09 19-25-26

  2. I then try and load the button WebComponent, breaking the application. You can now see that the box's module logged to the console, again.
    screenshot from 2018-05-09 19-25-55

  3. I then try and add another box WebComponent, it is not added and I also get a new error logged in my console (the total log you see in this screenshot was pasted above.)
    screenshot from 2018-05-09 19-26-06

@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 10, 2018

I created a detailed step by step descriptions to setup multiple projects and elements.

Unfortunately, this will just help to reproduce the bug, not it's not a fix, but easily adaptable if we find a solution,

Here is the all in one repo:
ng-elements-poc

@gkalpak as you can see in the attached description the polyfills are not included:

Angular Elements Build Setup

The dev-kit has a package available for building web components with angular.
You can use the @angular/elements package for this.
Here you can follow a step by step setup for angular elements running standalone or in another angular app.

Setup a new project

  1. Create a new project. Run ng new ng-elements-poc in the console.

  2. Switch into you new directory: cd ng-elements-poc

  3. You should now be able to test it by running ng serve --open in the console.

Setup WebComponents

  1. Run ng add @angular/elements in the console.
    The cli will install some packages to your package.json:
// package.json

{
[...] 
  dependencies: {
    [...]
    "@angular/elements": "^6.0.0",
    "document-register-element": "^1.7.2"
  }
[...]
}

And add a script to your projects scripts config in angular.json:

// angular.json

{
[...]
  "projects": {
    "ng-elements-poc": {
    [...]
      "scripts": [
        {
          "input": "node_modules/document-register-element/build/document-register-element.js"
        }
      ],
      [...]
    },
    [...]
  },
  [...]
}
  1. Test the if everything is still working: ng serve

Setup application for standalone web component

  1. Generate a new project in which we can test an elements setup.
    Run ng generate application my-first-element in the console.

  2. Copy the script in your angular.json (mentioned in step Setup WebComponents:1.) from project ng-elements-poc to my-first-element scripts:

// angular.json

{
[...]
  "projects": {
    [...]
    "my-first-element": {
    [...]
      "scripts": [
        {
          "input": "node_modules/document-register-element/build/document-register-element.js"
        }
      ],
      [...]
    },
    [...]
  },
  [...]
}
  1. Test it: ng serve --project my-first-element

  2. Setup a script in your package.json to start the my-first-element application:

// package.json

{
  [...] 
  scripts: {
  [...]
   "first-element:start": "ng serve --project my-first-element",
  },
  [...]
}
  1. Test it by running npm run first-element:start.

  2. Now lets create a build task that we can use later on to generate the bundled web component file.
    Setup a script in your package.json to build the application.
    Note that we set the flag output-hashing to none to have the bundles always with the same file names.

// package.json

{
  [...] 
  scripts: {
  [...]
   "first-element:build": "ng build --prod --project my-first-element --output-hashing=none"
  },
  [...]
}
  1. Run the command and check the file names in the dist folder.

  2. You can also test the bundles directly. Therefore lets another package:

Install the http-server globally:
npm install http-server -g

Now we can run http-server .\dist\my-first-element\ in our root folder.
As stated in the console we can now access the serve file under 127.0.0.1:8080.

Starting up http-server, serving .\dist\my-first-element\
Available on:
  http://192.168.43.58:8080
  http://127.0.0.1:8080

Create component and bootstrapping

We have setup a new project to test standalone web components. Now lets create one.

  1. Create a component called first-element and set viewEncapsulation to Native:
    ng generate component first-element --project my-first-element --spec=false --viewEncapsulation=Native

  2. Remove AppComponent from you project.

  • delete app.component.ts, app.component.html, app.component.css, app.component.spec.ts
  • remove all references in app.module.ts
  1. In app.module.ts remove the empty settings and add FirstElementComponent to the entryComponents
// projects/my-first-element/src/app/app.module.ts

import { FirstElementComponent } from './first-element/first-element.component';

@NgModule({
  declarations: [FirstElementComponent],
  imports: [BrowserModule],
  // providers: [],
  // bootstrap: [],
  entryComponents: [FirstElementComponent]
})
  1. Implement the bootstrapping logic for your component.
// projects/my-first-element/src/app/app.module.ts

import {Injector, [...]} from '@angular/core';
import {createCustomElement, NgElementConfig} from '@angular/elements';

@NgModule({
[...] 
})
export class AppModule {
  constructor(private injector: Injector) {

  }

  ngDoBootstrap() {
    const config: NgElementConfig = {injector: this.injector};
    const ngElement = createCustomElement(FirstElementComponent, config);

    customElements.define('app-first-element', ngElement);
  }

}
  1. In your index.html replace <app-root></app-root> with <app-first-element></app-first-element>:
<!-- projects/my-first-element/src/index.html --> 

[...]
<body>
  <!-- vvv REMOVE vvv
  <app-root></app-root>
  vvv ADD vvv -->
  <app-first-element></app-first-element>
</body>
</html>
  1. Test your web component.
    Run npm run first-element:start

  2. You can also setup a new script in package.json to bundle the files to use your web component in another place.
    Let's introduce the bundle-standalone script.

// package.json

{
  [...]
  "first-element:bundle-standalone": "cat dist/my-first-element/{runtime,polyfills,scripts,main}.js > dist/my-first-element/my-first-element-standalone.js",
}
  1. Run npm run first-element:bundle-standalone in the console to test it.

Test web component in another angular app

  1. Setup new script in package.json to bundle the files for another angular project
// package.json

{
  [...]
  "first-element:bundle-ng": "cat dist/my-first-element/{runtime,main}.js > dist/my-first-element/my-first-element-ng.js",
}
  1. Run npm run first-element:bundle-ng in the console to test it.

  2. Copy dist/my-first-element/my-first-element-ng.js into
    src/assets/ng-elements to serve this file as an asset of your root project.

  3. In your root application ng-elements-poc open app.module.ts

Add the following to your ngModule decorator:


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

And insert following code to AppModule constructor

export class AppModule {

  constructor() {
    const scriptTag = document
          .createElement(`script`);
        scriptTag.setAttribute('src', 'assets/elements/my-first-element-ng.js');
        scriptTag.setAttribute('type', 'text/javascript');

    document.body.appendChild(scriptTag);
  }

}
  1. Add the html tag into your app.component.html
<!-- src/app/app.component.html -->

[...]
<app-first-element></app-first-element>

Using multiple element bundles in one app

Test test if we can use multiple elements we can test a multiple elements in the same bundle and b multiple elements in different bundles.

Let's start with b multiple elements in a different bundle.

  1. Create a new project name my-other-element. Do this by following the steps from Setup application for standalone web component and Create component and bootstrapping

  2. Create npm scripts for copying the files over into src/assets/elements

// package.json

{
  [...]
  "first-element:copy-bundle": "cat dist/my-first-element/my-first-element-ng.js > src/assets/ng-elements/my-first-element-ng.js",
  "other-element:copy-bundle": "cat dist/my-other-element/my-other-element-ng.js > src/assets/ng-elements/my-other-element-ng.js",
  "copy-bundles": "npm run first-element:copy-bundle && npm run other-element:copy-bundle"
}
  1. In your root application ng-elements-poc open app.module.ts

    Refactor the creation of the script into a separate function:

    export class AppModule {
    
      constructor() {
        const bundles = ['my-first-element-ng', 'my-other-element-ng'];
        
        bundles
         .forEach(name => document.body.appendChild(this.getScriptTag(name)));
       
      }
      
      getScriptTag(fileName: string): HTMLElement {
         const scriptTag = document
           .createElement(`script`);
     
         scriptTag.setAttribute('src', `assets/ng-elements/${fileName}.js`);
         scriptTag.setAttribute('type', 'text/javascript');
     
         return scriptTag;
      }
       
    }
  2. Add the html tag into your app.component.html

<!-- src/app/app.component.html -->

[...]
<app-other-element></app-other-element>
  1. Test it. Run following commands:
npm run first-element:build
npm run first-element:bundle-ng
npm run other-element:build
npm run other-element:bundle-ng
npm run copy-bundles
@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 10, 2018

@robwormald any ideas?

@METACEO

This comment has been minimized.

Copy link
Author

commented May 11, 2018

Thank you for the clear reproduction @BioPhoton !

@gkalpak

This comment has been minimized.

Copy link
Member

commented May 11, 2018

Taking a closer look at the demo, it seems to be trying to use the elements as standalone Angular "mini-apps" (for example each includes BrowserModule and does its own bootstrapping). This is likely what is causing the issue. AFAIK, this is not supported just yet (tagging @robwormald, @andrewseguin to confirm).

What is supported is including the elements from within an Angular app, which loads the custom element module factories and bootstraps them (sharing the main app's injector among the custom element modules).
This is what we currently do in angular.io btw (and here is the loader we use for reference).

@BioPhoton

This comment has been minimized.

Copy link
Contributor

commented May 11, 2018

THX for the heads up!
The link is very helpful!

@METACEO

This comment has been minimized.

Copy link
Author

commented May 11, 2018

Thank you @gkalpak - I like how you described these, standalone Angular "mini-apps" are exactly what I'm trying to consume. I thought this would be supported since React, Wordpress and/or any other website could consume Angular Elements, but it's looking like we can't support multiple together. E.g. I can't use Firebase's example polling WebComponent along with my simple Box WebComponent from above.

Specifically, my circumstance includes an Angular application trying to consume WebComponent artifacts from other teams and third-parties. This Angular application has no awareness of the WebComponents it could load but with configuration it would lazily load specific WebComponents during run-time (which I believe is counter to what angular.io is doing - the registry is an array of routes used as a "hack" for bundling/lazy loading.)

Some of these WebComponents my application will consume may not be built from Angular Elements, but I know of many teams who are already developing with Angular who would like to package and publish their components for my team to use (we're all excited for what Angular Elements promises to bring!)

@gkalpak

This comment has been minimized.

Copy link
Member

commented May 11, 2018

Just to be clear, being able to run multiple Angular Elements in any app (Angular or not), IS one of the usecases that @angular/elements aims to support (in fact, it might be its primary/most common usecase).

It's just that it isn't possible in this first stable implementation (we've tried to keep it simple and focus on one (simpler) usecase). It is definitely something that will be worked on in the near future (subjective personal estimation 😁).

@gkalpak

This comment has been minimized.

Copy link
Member

commented Oct 18, 2018

FWIW, removing ^/~ shouldn't make a difference as lonk as you use the lockfile (package-lock.json, yarn.lock, etc.) provided in each project.

@liorwohl

This comment has been minimized.

Copy link

commented Oct 21, 2018

I have only one angular-element that i want to use inside a regular Angular 7.0.0 app. I'm getting the same error. "ERROR DOMException: Failed to execute 'define' on 'CustomElementRegistry': this name has already been used with this registry"

If I load the webcomponent js with defer (or load it in body below the angular app js files) its working at first, but after refresh or navigating to a non-angular page and then returning to the angular page, the attributes of the webcomponent dont work, only the tag without its properties.

I'm also using the same webcomponent in an Angular 6 app and AngularJS app and there its working without this bug.

It's not zone.js related, I had separate zone.js file and now i'm using git://github.com/JiaLiPassion/zone.js#duplicate-patch-dist
and getting the same error.
and i'm using elements-zone-strategy in the webcomponent.

removing node_modules didnt change anything.

@liorwohl

This comment has been minimized.

Copy link

commented Oct 22, 2018

I found an ugly workaround, setting the attributes with JS setAttribute is working just fine.

ngOnInit () {
this.fixAngularElementsBug();
}

fixAngularElementsBug () {
window.addEventListener('hashchange', () => { this.setCommonLayoutAttributes(); });
this.setCommonLayoutAttributes();
}
setCommonLayoutAttributes () {
let commonLayout = document.getElementsByTagName('common-layout')[0];
commonLayout.setAttribute('current-page', this.getPageName());
if (this.isShowConfig()) { commonLayout.setAttribute('show-config', 'true'); }
if (this.isLoading()) { commonLayout.setAttribute('is-loading', 'true'); }
if (this.enableRestTester) { commonLayout.setAttribute('enable-rest-tester', 'true'); }
}

< common-layout>< !-- [currentPage]="getPageName()" [showConfig]="isShowConfig()" [isLoading]="isLoading()" [enableRestTester]="enableRestTester">-- >

PAGE CONTENT

</ common-layout>

@Lowell20

This comment has been minimized.

Copy link

commented Nov 9, 2018

simple enough
if (!customElements.get('element')) customElements.define('element', cardElement);

@sri1980

This comment has been minimized.

Copy link

commented Jan 27, 2019

My case is slightly different. I have two angular elements defined in one project (module). It works fine in chrome, both elements I am able to load dynamically (lazy loading). In edge browser only first loading element works fine, second element does not recognize as angular element. No errors in console. Any idea what’s going on here?

@liorwohl

This comment has been minimized.

Copy link

commented Jan 28, 2019

I had another slightly different case, I have a webcomponent used by angular 1,6,7 apps. In the angular 6 app when in prod mode something caused the angular 6 app functions like ngOnInit or the bootstrapping to always run two times. the way I fixed it was to use "ngx-build-plus" as the builder in the angular.json of the webcomponent with "ng build --single-bundle true --prod" in package.json.
The default way webpack/angular-cli builds the app there are functions in the global scope apparently and ngx-build-plus builds it in a more separated way.

@f1nality

This comment has been minimized.

Copy link

commented Feb 2, 2019

@robwormald why this issue is closed? I didn't find any solutions how to solve this problem.

@unstephenk

This comment has been minimized.

Copy link

commented Feb 12, 2019

I am experiencing this same issues in Angular 8 beta.

@sri1980

This comment has been minimized.

Copy link

commented Feb 13, 2019

I am able to solve this problem with approach as below.

https://medium.com/@sri1980/multiple-angular-elements-apps-loading-in-one-window-7bcc95887ff4

@Yogi2001

This comment has been minimized.

Copy link

commented Feb 22, 2019

a renaming of the window.webpackJsonp variable in the bundle to a unique (e.g.: window.webpackJsonp+ElementName)
has solved that issue for me

@unstephenk

This comment has been minimized.

Copy link

commented Feb 22, 2019

npx-build-plus worked for me but then we tried using different versions of angular and updated to the newest npx-build-plus package. Now I can only get one element to work.

@sri1980

This comment has been minimized.

Copy link

commented Feb 24, 2019

@unstephenk Can you please elaborate what versions of angular you are using?

here is my obersavations.

ngx-build-plus works fine to load multiple angular elements, we will not run into any issues even though multiple elements/apps use the same variable name (webpackJsonp). The reason is some how browser is able to differeniate these variables when we use single bundle approach. But styles was getting messed up.

If you are using angular 4+ versions then renaming webpackJsonp variable using customWebpackConfig would solve the problem, with this approach you can load multiple angular elements successfully.

Please share a sample repo, that would help to look into the problem.

@unstephenk

This comment has been minimized.

Copy link

commented Feb 27, 2019

@sri1980 Will look into this.

@vkvarma

This comment has been minimized.

Copy link

commented Mar 1, 2019

@METACEO Cool. Feel free to provide feedback.

@manfredsteyer I have one question ngx-build-plus able to separate it out the two elements and
that shares the common angular and third party libs e.g. core-js, zone and angular lib.
But how they will share the same store if both elements have their own NGRX store, then it will create the two instance of store for two different elements. How to combine those two NGRX store?

@wzc570738205

This comment has been minimized.

Copy link

commented Mar 28, 2019

simple enough
if (!customElements.get('element')) customElements.define('element', cardElement);

it works!cool~

@danielehrhardt

This comment has been minimized.

Copy link

commented Apr 4, 2019

Now we are at Angular 7. Something new here?

@tobang

This comment has been minimized.

Copy link

commented Apr 15, 2019

@wzc570738205 Where should this code be placed?

@tradeli

This comment has been minimized.

Copy link

commented Apr 18, 2019

For anyone wondering, this issue is closed and the explanation is here: #23732 (comment)

@robwormald

This comment has been minimized.

Copy link
Member

commented Apr 18, 2019

@juliechen727

This comment has been minimized.

Copy link

commented Apr 24, 2019

Had a chance to look closer at this today. As suspected, it's a bundling issue. The root cause is that webpack (which drives the CLI) uses a runtime: webpackJsonp global, and you're overwriting that each time you load another bundle (which also defines webpackJsonp) - See webpack/webpack#3791 (comment). The CLI doesn't expose this option (things like this are why we are not supporting this use case yet). You could (though I don't recommend it) manually rename that global webpackJsonp in each bundle to something unique.

You're also duplicating all the polyfills, which is likely to cause all kinds of unexpected results, as they're shimming native APIs and overwriting them at various times. Further, bundling a copy of all the angular packages into each bundle seems suboptimal.

For the moment, if you want to do this sort of use case, your likely best option is going to be to use something like rollup to build UMD bundles, that rely on angular's UMD bundles and exclude the angular source from each element's package. It's going to take some manual work.

Alternately, don't use the CLI to build the individual elements into binaries, treat them as libraries and bring them into the build properly, so you only have one webpack runtime.

Again, we're working to enable this use case for v7, so I'm going to close this (as its not actually an issue with Angular core)

Hi, just wonder whether multiple Angular elements on one single page is supported by latest Angular 7 yet? Thanks!

@manfredsteyer

This comment has been minimized.

Copy link

commented Apr 24, 2019

It's possible. However, if you compile them separately, it's not officially supported and you may end up with big bundles. The time after Ivy provides a remedy ...

@juliechen727

This comment has been minimized.

Copy link

commented Apr 24, 2019

It's possible. However, if you compile them separately, it's not officially supported and you may end up with big bundles. The time after Ivy provides a remedy ...

Thanks! Hopefully Ivy will be released soon. We are building micro front end demo by using Angular elements. Being able to support multiple Angular elements from independent projects on the same page is a key thing to us.

@fr0

This comment has been minimized.

Copy link

commented Jun 29, 2019

ngx-builds-plus is the right way to do this for the moment. Once ivy goes out we’ll bring out some new stuff for Elements :)

Again, we're working to enable this use case for v7

Not sure how to reconcile these two statements. We're long past v7, so why do we have to resort to using a separate plugin?

salilvnair pushed a commit to salilvnair/flopkart-mfe-elements that referenced this issue Jul 12, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.