4

Given three @angular projects all using v6.1.9: host, alpha, and beta

alpha and beta create and define a web component each using @angular/elements as such:

constructor(private injector: Injector) {}

ngDoBootstrap() {
  const config: NgElementConfig = { injector: this.injector };
  const component= createCustomElement(component, config);
  customElements.define("alpha-component", component); // beta-component respectively
}

alpha and beta are built using ng build --prod --output-hashing none and then a post build script is ran to concatenate the resulting files in the order of: scripts.js, styles.js, runtime.js, main.js.

polyfills.js is skipped because main.ts will check if the polyfills used are already defined when the library is loaded (to avoid trying to redefine zones.js for example).

The resultant bundles are alpha-component.bundle.js and beta-component.bundle.js.

host references the above bundles in the <head> of index.html with <script defer> tags.

If the bundles are referenced in the order of alpha then beta, I will see alpha trying to bootstrap twice; In the reverse order, I will see beta trying to bootstrap twice.

Since the first referenced bundle attempts to bootstrap twice, it attempts to define the web component for the bundle twice, causing an error, and never registering the second referenced bundle's web component.

The goal is to be able to create many web components using @angular and then consuming them within other @angular or insert framework here technologies.

Collin Stevens
  • 737
  • 9
  • 19

3 Answers3

5

unfortunatelly, concatination of bundles does not work here, b/c webpack is using a global variable. The variable created by the alpha-bundle would be overwritten by the variable of the beta-bundle.

You could rename this variable in the bundles or you could go with [1] and it's --single-bundle switch.

To get started, forget about what the readme says about externals. This is an further optimization technqiue, where the host, alpha and beta could share the same libs. This prevents you from loading Angular several times.

Perhaps, also my blog series about Angular Elements is interesting for you [2].

Best wishes, Manfred

[1] https://github.com/manfredsteyer/ngx-build-plus

[2] https://www.softwarearchitekt.at/post/2018/07/13/angular-elements-part-i-a-dynamic-dashboard-in-four-steps-with-web-components.aspx

Manfred Steyer
  • 479
  • 3
  • 12
  • I'm not sure where the --single-bundle switch exists, is it for angular or a webpack feature? A google search did not yield any relevant results. As far as the webpack variable, I'm assuming it is the webpackJsonp variable that was referenced in https://github.com/angular/angular/issues/23732. I've just now put post build scripts to rename this variable in each bundle to a "unique" name and I'm able to host two separately built and bundled angular web components in an angular project. I'll take a look at your blog post series! I was hoping with v7 we would have an official way to do this. – Collin Stevens Oct 23 '18 at 19:57
  • 2
    After posting, I realized that the --single-bundle switch exists on the ngx-build-plus lib. – Collin Stevens Oct 23 '18 at 20:00
  • This saved me a lot of time, as I was digging into some very strange solutions (see https://github.com/angular/angular/issues/35429). All I had to do is to add ngx-build-plus and use the --single-bundle option to be able to run 2 different external Angular Elements (v 10.1.2) on the same page. Thank you so much Manfred, I could not believe how easy it was to get this working :) – fox Sep 23 '20 at 20:22
1

I use Manfred Steyer advices and it works so I will develop here his answer by giving more detailed working example (based on my case - Angular 8, I do it on MacOs):

On your alpha web-commponent (which is separate angular project <alpha>) 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 main project directory) and then in package.json on key scripts I add/edit key:

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

and now I build web component by:

npm run build:alpha:externals

this will create file dist/alpha.js - in same way you build beta.js (in beta project) - and in your host you can add both to your index.html by

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

and both should work at the same time

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

I used angular custom builder to export the built angular bundle with unique namespace using custom webpack configuration for angular builder.

  1. Install @angular-builders/custom-webpack package

  2. Create custom webpack config file. eg. extra-webpack.config.js.

    module.exports = {
    output: {
        chunkLoadingGlobal: 'webpackChunkModuleName',
        library: 'ModuleName'
    }
    

Provide your application name or any unique name for the chunkLoadingGlobal & library.

Note: These properties are as per webpack 5 which is used in angular 12+. Please refer webpack documentation for older versions of webpack.

  1. Use @angular-builders/custom-webpack as your builder in place of @angular-devkit/build-angular and add customWebpackConfig object in angular.json.
"builder": "@angular-builders/custom-webpack:browser",
    .....
    "customWebpackConfig": {
       "path": "extra-webpack.config.js",
        "mergeStrategies": {
           "externals": "prepend"
         }
     }
  1. Now when you run ng build, your application would be built using custom builder with custom webpack configuration. Similar configuration for other application exported as web components and multiple web components can be used in same container application without any error or namespace collisions/contamination.
Shashi Kiran
  • 367
  • 3
  • 9