Is it possible to use different angular elements built with different versions of Angular
Asked Answered
A

1

11

I would like to know if it is possible to use different angular elements (custom elements) built with different versions of Angular. I have heard that zone.js was polluting the global scope.

Thanks for your answer.

Assurbanipal answered 10/12, 2019 at 10:51 Comment(0)
L
12

Yes, you have heard it correct. We cannot use multiple angular elements if each angular element created from a specific version is trying to load zonejs.

Having said that it is 100% possible to have multiple angular elements of different versions on a single page. All we need to take care of is loading zone js only once and sharing it across all the web-components(Angular Elements).

While bootstrapping multiple elements we can add the logic of not loading/patching zonejs if already loaded as below:

Remove zonejs polyfill from polyfill.ts for all Angular Elements

Create a file in main.ts level. Let's say bootstraper.ts :

export class Bootstrapper {
  constructor(
    private bootstrapFunction: (bootstrapper: Bootstrapper) => void
  ) {}

  /**
   * Before bootstrapping the app, we need to determine if Zone has already
   * been loaded and if not, load it before bootstrapping the application.
   */
  startup(): void {
    console.log('NG: Bootstrapping app...');

    if (!window['Zone']) {
      // we need to load zone.js
      console.group('Zone: has not been loaded. Loading now...');
      // This is the minified version of zone
      const zoneFile = `/some/shared/location/zone.min.js`;

      const filesToLoad = [zoneFile];

      const req = window['require'];
      if (typeof req !== 'undefined') {
        req(filesToLoad, () => {
          this.bootstrapFunction(this);
          console.groupEnd();
        });
      } else {
        let sequence: Promise<any> = Promise.resolve();
        filesToLoad.forEach((file: string) => {
          sequence = sequence.then(() => {
            return this.loadScript(file);
          });
        });

        sequence.then(
          () => {
            this.bootstrapFunction(this);
            console.groupEnd();
          },
          (error: any) => {
            console.error('Error occurred loading necessary files', error);
            console.groupEnd();
          }
        );
      }
    } else {
      // zone already exists
      this.bootstrapFunction(this);
    }
  }

  /**
   * Loads a script and adds it to the head.
   * @param fileName
   * @returns a Promise that will resolve with the file name
   */
  loadScript(fileName: string): Promise<any> {
    return new Promise(resolve => {
      console.log('Zone: Loading file... ' + fileName);
      const script = document.createElement('script');
      script.src = fileName;
      script.type = 'text/javascript';
      script.onload = () => {
        console.log('\tDone');
        resolve(fileName);
      };
      document.getElementsByTagName('head')[0].appendChild(script);
    });
  }
}

And in main.ts we can change the bootstrap logic to the below one :

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { Bootstrapper } from './bootstraper';
const bootstrapApp = function(): void {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .then(() => {})
    .catch(err => console.error(err));
};

const bootstrapper = new Bootstrapper(bootstrapApp);
bootstrapper.startup();

This way we can definitely create multiple Angular Elements (Web Components) and use in a SPA.

Thanks

Lioness answered 10/12, 2019 at 11:6 Comment(2)
Really thank you for this answer. Can a specific version of Zone.js be compatible either with Angular 6, 7 or 8 ?Assurbanipal
Yes we have Angular 7 and Angular 6 using the latest zonejs version with no issues. If the answer helped you please upvote.Lioness

© 2022 - 2024 — McMap. All rights reserved.