Carica i nuovi moduli in modo dinamico in fase di esecuzione con Angular CLI e Angular 5

Attualmente sto lavorando a un progetto che è ospitato su un server client. Per i nuovi ‘moduli’ non è prevista la ricompilazione dell’intera applicazione. Detto questo, il client desidera aggiornare i moduli router / lazy-load in runtime . Ho provato diverse cose ma non riesco a farlo funzionare. Mi stavo chiedendo se qualcuno di voi sa cosa potrei ancora provare o cosa mi è mancato.

Una cosa che ho notato, la maggior parte delle risorse che ho provato, usando angular cli, sono messe in bundle in blocchi separati dal webpack di default quando si costruisce l’applicazione. Che sembra logico in quanto utilizza la suddivisione del codice webpack. ma cosa succede se il modulo non è ancora noto al momento della compilazione (ma un modulo compilato è memorizzato da qualche parte su un server)? Il raggruppamento non funziona perché non riesce a trovare il modulo da importare. E l’uso di SystemJS caricherà i moduli UMD ogni volta che verranno trovati sul sistema, ma saranno anche raggruppati in un blocco separato tramite webpack.

Alcune risorse ho già provato;

  • dinamico-remote-componente-loader
  • Modulo-loading
  • Caricamento di moduli da diversi server in fase di esecuzione
  • Come caricare componenti esterni dinamici nell’applicazione Angolare
  • Implementazione di un’architettura plug-in / sistema di plugin / framework pluggable in Angular 2, 4, 5, 6
  • Angular 5 – load modules (che non sono noti al momento della compilazione) dynamicmente in fase di esecuzione
  • https://medium.com/@nikolasleblanc/building-an-angular-4-component-library-with-the-angular-cli-and-ng-packagr-53b2ade0701e
  • Alcuni altri che riguardano questo argomento.

Qualche codice che ho già provato e implementato, ma che non funziona in questo momento;

Estensione del router con il normale file module.ts

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

Normal SystemJS Importazione del bundle 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); }); }); 

Importa modulo esterno, non funziona con 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); }); 

Usa 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); }); 

Ho provato a caricare un modulo realizzato 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(); 

Esempio con SystemJsNgModuleLoader funziona solo quando il Modulo è già fornito come percorso “pigro” nel RouterModule dell’app (che lo trasforma in un blocco quando viene creato con il webpack)

Ho trovato molte discussioni su questo argomento su StackOverflow qua e là e le soluzioni fornite sembrano davvero buone per caricare dynamicmente moduli / componenti se noti in anticipo. ma nessuno è adatto per il nostro caso d’uso del progetto. Per favore fatemi sapere cosa posso ancora provare o tuffarmi.

Grazie!

EDIT: ho trovato; https://github.com/kirjs/angular-dynamic-module-loading e proveremo a farlo.

AGGIORNAMENTO: Ho creato un repository con un esempio di caricamento dei moduli in modo dinamico utilizzando SystemJS (e utilizzando Angular 6); https://github.com/lmeijdam/angular-umd-dynamic-example

Stavo affrontando lo stesso problema. Per quanto ho capito fino ad ora:

Webpack mette tutte le risorse in un pacchetto e sostituisce tutto System.import con __webpack_require__ . Pertanto, se si desidera caricare dynamicmente un modulo in fase di esecuzione utilizzando SystemJsNgModuleLoader, il caricatore cercherà il modulo nel pacchetto. Se il modulo non esiste nel pacchetto, si otterrà un errore. Webpack non chiederà il server per quel modulo. Questo è un problema per noi, dal momento che vogliamo caricare un modulo che non conosciamo al momento della compilazione / compilazione. Ciò di cui abbiamo bisogno è caricatore che caricherà un modulo per noi in fase di runtime (lazy e dynamic). Nel mio esempio, sto usando SystemJS e Angular 6 / CLI.

  1. Installa SystemJS: npm install systemjs -save
  2. Aggiungilo a angular.json: “script”: [“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"); } } 

Come puoi vedere, possiamo dire a SystemJS quali moduli esistono già nel nostro pacchetto. Quindi non abbiamo bisogno di caricarli di nuovo ( SystemJS.set ). Tutti gli altri moduli che importiamo nel nostro componente my-dynamic-component (in questo esempio other ) verranno richiesti dal server in fase di esecuzione.

Credo che sia ansible utilizzare SystemJS per caricare un pacchetto UMD se si crea ed esegue l’applicazione principale utilizzando il webpack. Ho usato una soluzione che utilizza ng-packagr per creare un pacchetto UMD del modulo plug-in / addon dinamico. Questo github dimostra la procedura descritta: https://github.com/nmarra/dynamic-module-loading

Ho utilizzato la soluzione https://github.com/kirjs/angular-dynamic-module-loading con il supporto della libreria di Angular 6 per creare un’applicazione che ho condiviso su Github. A causa della politica aziendale, era necessario portarlo offline. Non appena le discussioni sono finite riguardo all’origine del progetto di esempio, la condividerò su Github!

AGGIORNAMENTO: può essere trovato un repo; https://github.com/lmeijdam/angular-umd-dynamic-example

Sì, è ansible caricare i moduli in modo pigro utilizzando i moduli come riferimenti nel router. Ecco un esempio https://github.com/start-angular/SB-Admin-BS4-Angular-6

  1. Innanzitutto accoppiare tutti i componenti che si stanno utilizzando in un singolo modulo
  2. Ora fai riferimento a quel modulo nel router e angular caricherà il tuo modulo nella vista.