javascript - without - using angularjs components in angular 2




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

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 deseja atualizar os módulos roteadores / lazy loaded em tempo de execução . Eu tentei várias coisas, mas não consigo fazer 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 recursos que eu tentei, usando cli angular, estão sendo empacotados em pedaços separados pelo webpack por padrão ao construir 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 compilação (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 agrupados em um bloco separado pelo webpack.

Alguns recursos que eu já tentei;

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<any>) => {
  console.log(moduleFactory);
  const entryComponent = (<any>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<any> = 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 parecem realmente boas de carregar módulos / componentes dinamicamente, se forem conhecidos 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 repositório 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 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 dinâmico. Este github demonstra o procedimento descrito: https://github.com/nmarra/dynamic-module-loading


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

O Webpack coloca todos os recursos 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 compilação / compilação. O que precisamos é de um carregador que carregue um módulo para nós em tempo de execução (preguiçoso e dinâmico). No meu exemplo, estou usando o SystemJS e o Angular 6 / CLI.

  1. Instalar 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: '<button (click)="load()">Load</button><ng-container #vc></ng-container>'
})
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: '<h1>Dynamic component</h1><button (click)="LoadMore()">LoadMore</button>'
})    
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.



Faça isso com a biblioteca angular 6 e faça o truque. Eu apenas experimento com ele e posso compartilhar módulo AOT angular autônomo com o aplicativo principal sem reconstruir última.

  1. No conjunto de bibliotecas angulares angularCompilerOptions.skipTemplateCodegen para false e depois da biblioteca de compilação, você obterá a fábrica do módulo.
  2. Depois disso construa um módulo umd com rollup como este: rollup dist/plugin/esm2015/lib/plugin.module.ngfactory.js --file src/assets/plugin.module.umd.js --format umd --name plugin
  3. Carregar fonte de texto umd bundle no aplicativo principal e eval-lo com o contexto do módulo
  4. Agora você pode acessar o ModuleFactory do objeto de exportação

Aqui https://github.com/iwnow/angular-plugin-example você pode encontrar como desenvolver o plugin com o edifício autônomo e AOT







angular5