Angular-cli: Dynamic Loading NgModule Support

Created on 23 Jan 2018  路  26Comments  路  Source: angular/angular-cli

Right now, there's only one official Angular CLI way to dynamically load NgModule; through the router configuration and the discovery process for lazy routes.

This discussion is not about dynamic loading of TypeScript files, which works properly already, using the import() keyword. This is exclusively when splitting code at the NgModule boundary, which isn't currently supported.

This has as a consequence that people have to use a fake route if they want to split at the NgModule boundary; e.g. AIO here. Although this work, it's a bad pattern that users shouldn't have to do.

The current suggested design is:

  1. Allow users to add entries to the lazy route from the configuration to emulate the ROUTES provider, then
  2. Use NgModuleFactoryLoader (see example in AIO here) to load the module, which supports both JIT and AOT.

Alternative design was to add refactoring/support of import() keyword, with some annotation support to tell if we should rename to import on AOT. This would require changes in the Angular Compiler which would only make this compatible with Angular 6, while the solution above is fully compatible with Angular 5.

/cc @IgorMinar @clydin @filipesilva @robwormald

devkibuild-angular faq feature

Most helpful comment

Is this issue on the roadmap? Will Ivy release help with that? We are using ui-router in our app and we are forced to include the entire @angular/router module in the bundle, so that vendor routing code size exceeds 60kb min/gzipped, which is insane.

With Ivy, we will be able to use import('module-name') for lazy-loading and rendering feature components without the router involved.

Jason Aden talks about it and shows a demo here:
Angular Ivy by example | Jason Aden | AngularConnect 2018 (timestamped)

But Ivy will probably not be production-ready before Angular 9.x according to @IgorMinar.

All 26 comments

Related existing discussion in https://github.com/angular/angular-cli/issues/4541, cc @christopherthielen

Hi @hansl, is this issue scenario work with the angular-cli latest version? If it work, could you please give some guide about it?

I have the exact same use case as @khaled-ansary and would be very much interested in a solution for this. I understand that tree shaking will not be possible in this case but this does not matter that much to me.

I would also like to use SystemJS and/or a custom NgModuleFactoryLoader implementation to lazy load modules, particularly from another origin. Is there a way of disabling the findLazyRoutes functionality or add an ignore list?

I got super exicted when I got this to work on dev server only to find out that it won't build because of this

Is this issue on the roadmap? Will Ivy release help with that? We are using ui-router in our app and we are forced to include the entire @angular/router module in the bundle, so that vendor routing code size exceeds 60kb min/gzipped, which is insane.

Is this issue on the roadmap? Will Ivy release help with that? We are using ui-router in our app and we are forced to include the entire @angular/router module in the bundle, so that vendor routing code size exceeds 60kb min/gzipped, which is insane.

With Ivy, we will be able to use import('module-name') for lazy-loading and rendering feature components without the router involved.

Jason Aden talks about it and shows a demo here:
Angular Ivy by example | Jason Aden | AngularConnect 2018 (timestamped)

But Ivy will probably not be production-ready before Angular 9.x according to @IgorMinar.

Very interested in this feature, Our project never uses routing and the main.js is about 5Mb. And the site is obviously heavy though. If we can lazyload modules that would be really amazing!!!

What Jason Aden talked about could also allow to bring something like a plugin system into your apps. A feature, that we have been waiting for a long time.

@mbeckenbach, totally agree, same here! I'd this request last year for a client project and was surprised (and then devastated) about how practically impossible it was to implement back then and the fact it seemed to never have been on the radar of Angular's development.

So this is definitely good news. 馃ぉ

@mblackritter
did you find a workarround for that feature?

We're thinking about using the library feature from nx workspace which supports libs that provide a lazy loading routing module. It should work for the moment but is not optimal as we need to recomile everytime we want to add a new plugin.

An alternative that we are thinking about is using angular elements to load our plugins into an existing angular app. Not sure yet if this is a good idea.

@mbeckenbach I tried to implement the concept based different discussions I read on Github issues, but that was possible only with combination of AOT and JIT and has its own drawbacks.
Have a look at https://github.com/iamrakesh/ng-extensions-aot-and-jit

Just found this issue and realized that we can not build a new web app in a modular way with truly lazy loaded feature modules. Our scenario: the configuration which modules should be present or not is loaded at runtime from backend.

Hey all, I created a component that allows you to lazily load whenever you'd like. Or uses the lazy modules option for an project in the angular.json. it can solve all the issues that I've seen people talking about here.

Check it out: https://www.npmjs.com/package/@herodevs/lazy-af

For true post-deploy, dynamic module loading in Angular versions 2-7+, you have to look into SystemJsNgModuleLoader and NgModuleFactoryLoader.

@LayZeeDK that AND the lazyModules option in the project build options.

@aaronfrost Amazing job, thanks. But my modules don't have components. That are mostly services. Not an user triggers the lazy loading, but a config. service by fetching the config. from backend. Imagine a canvas graphic. Some graphics are rendered on initial loading from HTTP call, some at runtime from received messages via WebSocket. Now, I would like to configure what kind of graphic elements the user will see. Every graphic element has a module with renderer, message handling, store slice, etc. How to load such a lazy module programmatically? (not from user interactions)

@aaronfrost your solution works when you have all angular modules available/known at build phase.
Second usecase for which we seem not have a solution is when you dont know those modules at build phase, by this I mean these modules are built/compiled separately from the container application.

@iamrakesh for your use case wouldn鈥檛 work separately build angular elements work best? Then you would only need to know the script and tag name to load them?!

@denisyilmaz We did consider Angular Elements, but didn't evaluate thoroughly and decided to wait for Ivy to become available and decide then

@ova2 can you explain more what you are doing? I don't understand the use case. If the services are lazily loaded, and the lazily loaded code doesn't use the services that are being loaded, then who would be able to consume the service?

@aaronfrost Services are registered themself as multi providers when lazy loaded. They implement an interface from the core module. The core module is not lazy loaded. When a MQTT message has been received from backend, the logic in the core module gets these services over Injector, finds a certain service by received destinationName from the MQTT message and executes some logic on the found service. This just one scenario I'm evaluating right now...

If it's using multiproviders, that makes sense. This should load that as well, then. I will try it out and let you know.

@aaronfrost if you can publish this case of multiproviders online i am interested on it to learn (never faced this usecase)

Since Angular 8 we support using dynamic imports (import() syntax) in the router loadChildren split point and have deprecated the string syntax. We also automatically updated CLI projects using ng update. You can see the deprecation here: https://angular.io/guide/deprecations#loadchildren-string-syntax.

In non-Ivy projects, it ends up working the same as before. Internally we rewrite the import('./something.module.ts') to import('something.module.ngfactory.js') to make it work. This rewrite only happens in loadChildren properties. You can read more about it here: https://github.com/angular/angular-cli/blob/0d70565f9d80f1d765622eb8c8b2c3c701723599/packages/ngtools/webpack/src/transformers/import_factory.ts#L16-L55.

In Ivy projects however .ngfactory.js files do not exist. If you use import() on a Ivy project and that project was compiled with AOT, the NgModule will contain everything you need to dynamically load it outside the Angular router.

In Angular version 9 Ivy will become the default compiler. Using Ivy together with the import() syntax enables dynamically loading NgModules without any special tooling.

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings