Skip to content

Dynamic Loading NgModule Support #9343

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hansl opened this issue Jan 23, 2018 · 26 comments
Closed

Dynamic Loading NgModule Support #9343

hansl opened this issue Jan 23, 2018 · 26 comments
Labels
Milestone

Comments

@hansl
Copy link
Contributor

hansl commented Jan 23, 2018

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

@hansl hansl added feature Issue that requests a new feature type: discussion labels Jan 23, 2018
@filipesilva
Copy link
Contributor

filipesilva commented Jan 23, 2018

Related existing discussion in #4541, cc @christopherthielen

@ValentinFunk
Copy link

I suppose this is related? 3be17e7

@khaled-ansary
Copy link

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

@ManuelEssmeister
Copy link

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.

@mpicard
Copy link

mpicard commented Jul 1, 2018

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

@pshurygin
Copy link

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.

@LayZeeDK
Copy link

LayZeeDK commented Nov 16, 2018

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.

@webcat12345
Copy link

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!!!

@mbeckenbach
Copy link

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.

@mblackritter
Copy link

mblackritter commented Dec 9, 2018

@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. 🤩

@mbeckenbach
Copy link

@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.

@iamrakesh
Copy link

iamrakesh commented Jan 10, 2019

@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

@ngbot ngbot bot added this to the Backlog milestone Jan 24, 2019
@ova2
Copy link

ova2 commented Jan 27, 2019

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.

@aaronfrost
Copy link

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

@LayZeeDK
Copy link

LayZeeDK commented Jan 28, 2019

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

@aaronfrost
Copy link

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

@ova2
Copy link

ova2 commented Jan 28, 2019

@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)

@iamrakesh
Copy link

iamrakesh commented Jan 28, 2019

@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.

@denisyilmaz
Copy link

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

@iamrakesh
Copy link

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

@aaronfrost
Copy link

@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?

@ova2
Copy link

ova2 commented Jan 28, 2019

@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...

@aaronfrost
Copy link

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

@nasreddineskandrani
Copy link

nasreddineskandrani commented Jan 30, 2019

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

@filipesilva
Copy link
Contributor

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:

/**
* Given this original source code:
*
* import { NgModule } from '@angular/core';
* import { Routes, RouterModule } from '@angular/router';
*
* const routes: Routes = [{
* path: 'lazy',
* loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule).
* }];
*
* @NgModule({
* imports: [RouterModule.forRoot(routes)],
* exports: [RouterModule]
* })
* export class AppRoutingModule { }
*
* NGC (View Engine) will process it into:
*
* import { Routes } from '@angular/router';
* const ɵ0 = () => import('./lazy/lazy.module').then(m => m.LazyModule);
* const routes: Routes = [{
* path: 'lazy',
* loadChildren: ɵ0
* }];
* export class AppRoutingModule {
* }
* export { ɵ0 };
*
* The importFactory transformation will only see the AST after it is process by NGC.
* You can confirm this with the code below:
*
* const res = ts.createPrinter().printNode(ts.EmitHint.Unspecified, sourceFile, sourceFile);
* console.log(`### Original source: \n${sourceFile.text}\n###`);
* console.log(`### Current source: \n${currentText}\n###`);
*
* At this point it doesn't yet matter what the target (ES5/ES2015/etc) is, so the original
* constructs, like `class` and arrow functions, still remain.
*
*/
.

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.

@angular-automatic-lock-bot
Copy link

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.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Nov 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests