8

Is it possible to reference a module (already compile in umd or es format) and load it dynamically in a already compiled angular application?

  1. Main shell application hosted at: http://plugin_shell.mydomain.com
  2. A module (with a bunch of components, services, etc): compiled code is hosted at another url. let say: http://plugins/modules/myfirstplugin.umd.js
  3. When shell load. Load all modules, render specific components, refer to services, use class etc.

I tried to load the module with SystemJsNgModuleLoader.load, but it does seem to work with this kind of use cases.

Thanks

EDIT: Same question (no answer): How to dynamically load external angular 2 module (like served from an external module.bundle.js)

ThePainnn
  • 341
  • 4
  • 15
  • You're talking about a compiled ng application (aka webpack fragments) but you're also mentioning systemJS, which one are you using? – Supamiu Aug 08 '17 at 14:41
  • Well the shell is a webpack application. All the module, must be dynamic, so they are currently compiled as umd module with rollup.The problem here is that module will be updated and publish at an url. The shell must load them (and have the last version available) from the url. – ThePainnn Aug 08 '17 at 14:44
  • https://github.com/webpack/webpack/issues/150 this might help you, but because I don't know how it can be done exactly, I won't post it as an answer. It seems like people achieved what you want to do on this issue. – Supamiu Aug 08 '17 at 14:46

1 Answers1

3

You can do it like this:

@Component({
  providers: [
    {
      provide: NgModuleFactoryLoader,
      useClass: SystemJsNgModuleLoader
    }
  ]
})
export class ModuleLoaderComponent {
  constructor(private _injector: Injector,
              private loader: NgModuleFactoryLoader) {
  }

  ngAfterViewInit() {
    this.loader.load('app/t.module#TModule').then((factory) => {
      const module = factory.create(this._injector);
      const r = module.componentFactoryResolver;
      const cmpFactory = r.resolveComponentFactory(AComponent);

      // create a component and attach it to the view
      const componentRef = cmpFactory.create(this._injector);
      this.container.insert(componentRef.hostView);
    })
  }
}

Read Here is what you need to know about dynamic components in Angular for more details. Specifically Dynamic module loading and compilation section.

Max Koretskyi
  • 85,840
  • 48
  • 270
  • 414
  • 2
    but this code is good when module are baked in the application that was compiled. My shell is already compiled and module/component comes from another source entirely. For example I am trying to do in dev: `const url: string = 'http://localhost:8082/core/core.umd.js'; System.import(url).then((module) => { ...` – ThePainnn Aug 08 '17 at 15:37
  • No, that module is separate. `SystemJsNgModuleLoader` compiles it on the client – Max Koretskyi Aug 08 '17 at 15:41
  • Ok but how do I load it from an url like mine? It always tell me "Cannot find module". I step into the code of System.import and the module is not in the list of available modules (which is obvious since it not hosted by the current shell). Thanks for your replies it's appreciated :) – ThePainnn Aug 08 '17 at 16:00
  • can you show the contents what is returned by the url `http://plugins/modules/myfirstplugin.umd.js`? many on plunker create a file – Max Koretskyi Aug 08 '17 at 16:05
  • that will be hard, it's not "open source" really. I'll try something. But the umd.js is basically a module that has been compiled with ngc and bundle with rollup. It's then copied to node_modules, so we can do `import { mycomponent } from 'myplugin';` for example. This part works, but I need to make it dynamic so we could replace or add any plugin dynamically without recompiling the main shell. – ThePainnn Aug 08 '17 at 16:17
  • well, I need to see that module, what it exports, don't need everything, just a part, rename things – Max Koretskyi Aug 09 '17 at 04:59
  • Yep I know, I'll strip down a umd module for you. Need to validate with the company first. Moreover they are blocking plunker (IT politics...). maybe jsfiddle will do the thing. – ThePainnn Aug 10 '17 at 12:41
  • Here you go: https://jsfiddle.net/xou33jyz/ This won't compile, it's very stripped down. – ThePainnn Aug 10 '17 at 13:16
  • these are not AOT compiled classes – Max Koretskyi Aug 10 '17 at 14:48
  • 1
    I have a similar requirement @ThePainnn were you able to solve this issue. if so please share the details. Thanks – swingmicro Aug 17 '17 at 07:23
  • @swingmicro, the solution I posted works. I use it in my code – Max Koretskyi Aug 17 '17 at 07:32
  • @Maximus your solution works if the module is in the same application. but i have a similar requirement like thePainn. where i am trying to use the umd bundle of another application and load module of it in base application – swingmicro Aug 17 '17 at 09:19
  • @swingmicro, that's what I'm doing as well. `his.loader.load('app/t.module#TModule')` can load any module from outside – Max Koretskyi Aug 17 '17 at 09:20
  • its giving me "cannot find module" error while i give like this this.loader.load("app2/lib/app2.bundle.js#myModule"); – swingmicro Aug 17 '17 at 11:38
  • Hi, sorry for the lack of response. I had a major 4 weeks much needed vacations. I've got the same error, could not find module, because it was not package initially with the app it's not in the module list that the System.import loader expect. Moreover, I've got much more in that module than just a component, they are services and other shared library that I depend on. For now, I use IFrame with url pointing to my other applications (or plugins) which are each bundles independantly with webpack. This setup kind of sucks. @Maximus and swingmicro did you guys make it work? – ThePainnn Sep 18 '17 at 14:15
  • Interestingly the graylog guys (https://github.com/Graylog2), seems to have dynamic plugin that you can load. They are not in a different url thought, but it seems possible since it load a bunch of .js (which are all the plugins) just after their main core. Each plugin register to the main core upon load by the browser and by using reflux and some metadata they can communicate with each other without being strong typed like Angular is. Unfortunately, it's done with React, which does not help me. I wonder if there another project that does the same thing, but with latest Angular tech. – ThePainnn Sep 18 '17 at 14:20
  • the approach I showed in my answer I use in my large application that loads 3rd party plugins – Max Koretskyi Sep 18 '17 at 14:27
  • See this: https://github.com/angular/angular-cli/issues/5981. In my case it's even worse since my module is hosted somewhere else (different url from the app, which I don't control). Furthermore, when you do load('app/t.module#TModule'), thats mean that the module was made available within your main app. It's not loaded yet, since you dynamically loaded it, but its available to angular-cli during at the ng build phase. In my case it would be more like .load('http://anotherurl.com/mymodule.js') that would load from http://mainapp.com/. – ThePainnn Sep 18 '17 at 15:37
  • _that the module was made available within your main app_ - that is not the case. Any URL can be passed to `.load` – Max Koretskyi Sep 18 '17 at 15:38
  • Do you have an example of this? I never have been able to make it work. If I do one webpack package, yes, just like in your article. But, as soon as I hosted somewhere else, nothing works. I followed debug the code over and over again to see what's it's doing internally and I can't see how this would work. – ThePainnn Sep 18 '17 at 15:47
  • 1
    @ThePainnn, Webpack can't do that, that's correct. I use SystemJS for that – Max Koretskyi Sep 18 '17 at 15:49
  • @AngularInDepth.com is there a workaround for it in Webpack (angular-cli)? it seems like a bug limitation. – meno Oct 03 '17 at 09:34
  • 1
    @meno, no workaround because Webpack isn't designed to be a module loader. I use both webpack and SystemJS in my project. – Max Koretskyi Oct 03 '17 at 10:02
  • @AngularInDepth.com thanks, any guide or tips on how can we use both? any help is appreciated! – meno Oct 03 '17 at 11:34
  • 1
    @meno, just use SystemJS as a separate library and package the App using webpack/angular-cli. See [this answer](https://stackoverflow.com/a/46328636/2545680) – Max Koretskyi Oct 03 '17 at 12:18
  • @AngularInDepth.com does this approach work in Angular5 as well? I've been looking for a solution to dynamic remote components+services module loading for a while and I'd like to know if your approach is viable with modern versions of angular :) – Cec Mar 05 '18 at 16:21
  • @AngularInDepth.com please can you clarify your statement `Webpack isn't designed to be a module loader`, we use webpack and it is used to load the modules (also the lazy loaded modules) unless I'm missing something. – ANewGuyInTown Jul 18 '18 at 02:58
  • 2
    @ANewGuyInTown, yes, it can load modules, but only those known at the build time. It can't load modules dynamically similar to how [import()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports) does it – Max Koretskyi Jul 18 '18 at 06:37
  • thanks @AngularInDepth.com. How does lazy loaded modules work then? Isn't that loading modules dynamically? – ANewGuyInTown Jul 19 '18 at 02:04
  • @ANewGuyInTown, see [this video](https://www.youtube.com/watch?v=pERhnBBae2k) for the explanation how lazy loaded modules work in Angular. – Max Koretskyi Jul 19 '18 at 06:22
  • @AngularInDepth.com - what I'm not sure about is this line 'const cmpFactory = r.resolveComponentFactory(AComponent);' If it's a separate module coming from separate url, how do I handle this bit of code? – Mac_W Jul 20 '18 at 12:43
  • @Always_hungry, did you watch the video I linked in the previous comment? – Max Koretskyi Jul 23 '18 at 09:44
  • 1
    @ThePainnn @swingmicro if anyone still needed help, It worked fine for me with https://itnext.io/how-to-build-a-plugin-extensible-application-architecture-in-angular5-736890278f3f . @AngularInDepth.com is correct, you need to use `SystemJS` to load dynamic `plugin/component` – yogen darji Sep 17 '18 at 06:27
  • Thanks for the response, but we've switched completely to React + redux, it was trivial with this framework. Good to know it's worked in the end with Angular. – ThePainnn Sep 27 '18 at 14:37
  • @MaxKoretskyi Any new Updates, after angular moved to IVY and webpcak 5 is supported by angular ? any change we need in this regard or we still need to use systemJS too ? – GaurangDhorda May 09 '21 at 07:54