Carregar novos módulos dinamicamente em tempo de execução com o Angular CLI & Angular 5

Atualmente estou trabalhando em um projeto que está sendo hospedado em um servidor de clientes. Para novos ‘módulos’ não há intenção de recompilar o aplicativo inteiro. Dito isso, o cliente quer atualizar os módulos roteadores / lazy loaded em tempo de execução . Eu tentei várias coisas, mas não consigo fazê-lo funcionar. Eu queria saber se algum de vocês sabe o que eu ainda posso tentar ou o que eu perdi.

Uma coisa que eu notei, a maioria dos resources que eu tentei, usando cli angular, estão sendo empacotados em pedaços separados pelo webpack por padrão quando constroem o aplicativo. O que parece lógico, pois faz uso da divisão do código do webpack. mas e se o módulo não é conhecido ainda em tempo de compilation (mas um módulo compilado é armazenado em algum lugar em um servidor)? O agrupamento não funciona porque não pode encontrar o módulo para importar. E o uso do SystemJS carregará os módulos UMD sempre que forem encontrados no sistema, mas também serão empacotados em um bloco separado pelo webpack.

Alguns resources que eu já tentei;

  • carregador de componente remoto dynamic
  • carregamento de módulo
  • Carregando módulos do servidor diferente no tempo de execução
  • Como carregar componentes externos dynamics no aplicativo Angular
  • Implementando uma arquitetura de plug-in / sistema de plug-ins / estrutura conectável em Angular 2, 4, 5, 6
  • Módulos de carregamento angular 5 (que não são conhecidos em tempo de compilation) dinamicamente em tempo de execução
  • https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e
  • Alguns outros relacionados a esse tópico.

Algum código que eu já tentei e implementei, mas não estou trabalhando no momento;

Estendendo o roteador com o arquivo normal module.ts

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

Normal SystemJS Importação do pacote 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, não trabalhando com webpack (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); }); 

Tentei carregar um módulo feito com 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(); 

Exemplo com SystemJsNgModuleLoader só funciona quando o Módulo já é fornecido como rota ‘preguiçosa’ no RouterModule do aplicativo (o que o transforma em um trecho quando construído com o webpack)

Eu encontrei muita discussão sobre este tópico no StackOverflow aqui e ali, e desde que as soluções pareçam realmente boas de carregar módulos / componentes dinamicamente, se souberem de antemão. mas nenhum é adequado para nosso caso de uso do projeto. Por favor, deixe-me saber o que eu ainda posso tentar ou mergulhar.

Obrigado!

EDIT: eu encontrei; https://github.com/kirjs/angular-dynamic-module-loading e vai tentar.

ATUALIZAÇÃO: Eu criei um repository com um exemplo de carregamento de módulos dinamicamente usando o SystemJS (e usando o Angular 6); https://github.com/lmeijdam/angular-umd-dynamic-example

Eu estava enfrentando o mesmo problema. Até onde eu entendi até agora:

O Webpack coloca todos os resources em um pacote e substitui todos os System.import por __webpack_require__ . Portanto, se você deseja carregar um módulo dinamicamente em tempo de execução usando SystemJsNgModuleLoader, o utilitário de carga pesquisará o módulo no pacote configurável. Se o módulo não existir no pacote, você receberá um erro. O Webpack não pedirá ao servidor por esse módulo. Isso é um problema para nós, já que queremos carregar um módulo que não conhecemos em tempo de compilation / compilation. O que precisamos é de um carregador que carregue um módulo para nós em tempo de execução (preguiçoso e dynamic). No meu exemplo, estou usando o SystemJS e o Angular 6 / CLI.

  1. Instale o SystemJS: npm install systemjs –save
  2. Adicione-o 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 você pode ver, podemos dizer ao SystemJS quais módulos já existem em nosso pacote. Portanto, não precisamos carregá-los novamente ( SystemJS.set ). Todos os outros módulos que importamos em nosso my-dynamic-component (neste other exemplo) serão solicitados ao servidor em tempo de execução.

Eu acredito que isso é possível usando o SystemJS para carregar um pacote UMD se você construir e executar seu aplicativo principal usando o webpack. Eu usei uma solução que usa ng-packagr para construir um pacote UMD do módulo plugin / addon dynamic. Este github demonstra o procedimento descrito: https://github.com/nmarra/dynamic-module-loading

Eu usei a solução https://github.com/kirjs/angular-dynamic-module-loading com o suporte à biblioteca do Angular 6 para criar um aplicativo que compartilhei no Github. Devido à política da empresa, precisava ser colocado off-line. Assim que as discussões terminarem em relação ao exemplo de fonte do projeto, compartilharei no Github!

UPDATE: repo pode ser encontrado; https://github.com/lmeijdam/angular-umd-dynamic-example

Sim, você pode preguear os módulos de carga usando-os como módulos no roteador. Aqui está um exemplo https://github.com/start-angular/SB-Admin-BS4-Angular-6

  1. Primeiro, junte todos os componentes que você está usando em um único módulo
  2. Agora, consulte esse módulo no roteador e o carregamento será lento na vista.