Cargue nuevos módulos dinámicamente en tiempo de ejecución con Angular CLI y Angular 5

Actualmente estoy trabajando en un proyecto alojado en un servidor de clientes. Para los nuevos ‘módulos’ no hay intención de volver a comstackr toda la aplicación. Dicho esto, el cliente desea actualizar los módulos de enrutador / carga lenta en tiempo de ejecución . He intentado varias cosas pero no puedo hacer que funcione. Me preguntaba si alguno de ustedes sabe lo que aún podría intentar o lo que extrañé.

Una cosa que noté, la mayoría de los recursos que probé, usando angular cli, están siendo empaquetados en paquetes separados por paquete web de forma predeterminada al construir la aplicación. Lo cual parece lógico ya que hace uso de la división del código webpack. pero ¿qué pasa si el módulo todavía no se conoce en tiempo de comstackción (pero un módulo comstackdo se almacena en algún lugar en un servidor)? La agrupación no funciona porque no puede encontrar el módulo para importar. Y el uso de SystemJS cargará los módulos UMD siempre que se encuentren en el sistema, pero también se agrupan en una porción separada por el paquete web.

Algunos recursos que ya probé;

  • cargador dynamic-componente-remoto
  • módulo de carga
  • Cargando módulos desde diferentes servidores en tiempo de ejecución
  • Cómo cargar componentes externos dynamics en la aplicación angular
  • Implementación de una architecture de complemento / sistema de complemento / marco enchufable en Angular 2, 4, 5, 6
  • Angular 5: carga módulos (que no se conocen en tiempo de comstackción) dinámicamente en tiempo de ejecución
  • https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e
  • Algunos otros que relacionan este tema.

Algún código que ya probé e implementé, pero que no funciona en este momento;

Enrutador extendido con el archivo module.ts normal

this.router.config.push({ path: "external", loadChildren: () => System.import("./module/external.module").then( module => module["ExternalModule"], () => { throw { loadChunkError: true }; } ) }); 

Normal SystemJS Importación del paquete UMD

 System.import("./external/bundles/external.umd.js").then(modules => { console.log(modules); this.compiler.compileModuleAndAllComponentsAsync(modules['External']).then(compiled => { const m = compiled.ngModuleFactory.create(this.injector); const factory = compiled.componentFactories[0]; const cmp = factory.create(this.injector, [], null, m); }); }); 

Importar módulo externo, no funciona con paquete web (afaik)

 const url = 'https://gist.githubusercontent.com/dianadujing/a7bbbf191349182e1d459286dba0282f/raw/c23281f8c5fabb10ab9d144489316919e4233d11/app.module.ts'; const importer = (url:any) => Observable.fromPromise(System.import(url)); console.log('importer:', importer); importer(url) .subscribe((modules) => { console.log('modules:', modules, modules['AppModule']); this.cfr = this.compiler.compileModuleAndAllComponentsSync(modules['AppModule']); console.log(this.cfr,',', this.cfr.componentFactories[0]); this.external.createComponent(this.cfr.componentFactories[0], 0); }); 

Use SystemJsNgModuleLoader

 this.loader.load('app/lazy/lazy.module#LazyModule').then((moduleFactory: NgModuleFactory) => { console.log(moduleFactory); const entryComponent = (moduleFactory.moduleType).entry; const moduleRef = moduleFactory.create(this.injector); const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(entryComponent); }); 

Intentó cargar un módulo hecho con rollup

 this.http.get(`./myplugin/${metadataFileName}`) .map(res => res.json()) .map((metadata: PluginMetadata) => { // create the element to load in the module and factories const script = document.createElement('script'); script.src = `./myplugin/${factoryFileName}`; script.onload = () => { //rollup builds the bundle so it's attached to the window object when loaded in const moduleFactory: NgModuleFactory = window[metadata.name][metadata.moduleName + factorySuffix]; const moduleRef = moduleFactory.create(this.injector); //use the entry point token to grab the component type that we should be rendering const compType = moduleRef.injector.get(pluginEntryPointToken); const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(compType); // Works perfectly in debug, but when building for production it returns an error 'cannot find name Component of undefined' // Not getting it to work with the router module. } document.head.appendChild(script); }).subscribe(); 

Ejemplo con SystemJsNgModuleLoader solo funciona cuando el Módulo ya se proporciona como ruta ‘floja’ en el RouterModule de la aplicación (que lo convierte en una porción cuando se construye con un paquete web)

Encontré mucho debate sobre este tema en StackOverflow aquí y allá y las soluciones proporcionadas parecen realmente buenas para cargar módulos / componentes dinámicamente si se conocen desde el principio. pero ninguno es apropiado para nuestro caso de uso del proyecto. Por favor, avíseme sobre lo que todavía puedo intentar o bucear.

¡Gracias!

EDITAR: He encontrado; https://github.com/kirjs/angular-dynamic-module-loading y le dará una oportunidad.

ACTUALIZACIÓN: he creado un repository con un ejemplo de carga de módulos dinámicamente usando SystemJS (y usando Angular 6); https://github.com/lmeijdam/angular-umd-dynamic-example

Estaba enfrentando el mismo problema. Por lo que yo entiendo hasta ahora:

Webpack coloca todos los recursos en un paquete y reemplaza todo System.import con __webpack_require__ . Por lo tanto, si desea cargar un módulo dinámicamente en tiempo de ejecución utilizando SystemJsNgModuleLoader, el cargador buscará el módulo en el paquete. Si el módulo no existe en el paquete, obtendrá un error. Webpack no va a pedirle al servidor ese módulo. Este es un problema para nosotros, ya que queremos cargar un módulo que no conocemos durante el tiempo de comstackción / comstackción. Lo que necesitamos es un cargador que cargue un módulo para nosotros en tiempo de ejecución (flojo y dynamic). En mi ejemplo, estoy usando SystemJS y Angular 6 / CLI.

  1. Instalar SystemJS: npm install systemjs -save
  2. Añádelo a angular.json: “scripts”: [“node_modules / systemjs / dist / system.src.js”]

app.component.ts

 import { Compiler, Component, Injector, ViewChild, ViewContainerRef } from '@angular/core'; import * as AngularCommon from '@angular/common'; import * as AngularCore from '@angular/core'; declare var SystemJS; @Component({ selector: 'app-root', template: '' }) export class AppComponent { @ViewChild('vc', {read: ViewContainerRef}) vc; constructor(private compiler: Compiler, private injector: Injector) { } load() { // register the modules that we already loaded so that no HTTP request is made // in my case, the modules are already available in my bundle (bundled by webpack) SystemJS.set('@angular/core', SystemJS.newModule(AngularCore)); SystemJS.set('@angular/common', SystemJS.newModule(AngularCommon)); // now, import the new module SystemJS.import('my-dynamic.component.js').then((module) => { this.compiler.compileModuleAndAllComponentsAsync(module.default) .then((compiled) => { let moduleRef = compiled.ngModuleFactory.create(this.injector); let factory = compiled.componentFactories[0]; if (factory) { let component = this.vc.createComponent(factory); let instance = component.instance; } }); }); } } 

my-dynamic.component.ts

 import { NgModule, Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { Other } from './other'; @Component({ selector: 'my-dynamic-component', template: '

Dynamic component

' }) export class MyDynamicComponent { LoadMore() { let other = new Other(); other.hello(); } } @NgModule({ declarations: [MyDynamicComponent], imports: [CommonModule], }) export default class MyDynamicModule {}

other.component.ts

 export class Other { hello() { console.log("hello"); } } 

Como puede ver, podemos decirle a SystemJS qué módulos ya existen en nuestro paquete. Por lo tanto, no es necesario volver a cargarlos ( SystemJS.set ). Todos los otros módulos que importamos en nuestro my-dynamic-component (en este ejemplo, other ) se solicitarán al servidor en tiempo de ejecución.

Creo que esto es posible usando SystemJS para cargar un paquete de UMD si construyes y ejecutas tu aplicación principal usando el paquete web. Usé una solución que usa ng-packagr para construir un paquete UMD del módulo de complemento / complemento dynamic. Este github demuestra el procedimiento descrito: https://github.com/nmarra/dynamic-module-loading

Utilicé la solución https://github.com/kirjs/angular-dynamic-module-loading con la compatibilidad con la biblioteca de Angular 6 para crear una aplicación que compartí en Github. Debido a la política de la compañía, debe ser desconectado. ¡Tan pronto como terminen las discusiones sobre el origen del proyecto de ejemplo, lo compartiré en Github!

ACTUALIZACIÓN: se puede encontrar el repo; https://github.com/lmeijdam/angular-umd-dynamic-example

Sí, puede cargar módulos de carga lenta mediante la referencia como módulos en el enrutador. Aquí hay un ejemplo de https://github.com/start-angular/SB-Admin-BS4-Angular-6

  1. Primero empareja todos los componentes que estás usando en un solo módulo
  2. Ahora refiera ese módulo en el enrutador y angular cargará su módulo en la vista.