Skip to content

Dynamic Loading NgModule Support #9343

Closed

Description

@hansl

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

Activity

  1. filipesilva commented on Jan 23, 2018

    @filipesilva
    Contributor

    Related existing discussion in #4541, cc @christopherthielen

  2. ValentinFunk commented on Feb 24, 2018

    @ValentinFunk

    I suppose this is related? 3be17e7

  3. khaled-ansary commented on Feb 26, 2018

    @khaled-ansary

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

  4. ManuelEssmeister commented on Feb 27, 2018

    @ManuelEssmeister

    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.

  5. mpicard commented on Jul 1, 2018

    @mpicard

    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

  6. pshurygin commented on Nov 16, 2018

    @pshurygin

    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.

  7. LayZeeDK commented on Nov 16, 2018

    @LayZeeDK

    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.

  8. webcat12345 commented on Nov 27, 2018

    @webcat12345

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

  9. 6 remaining items

  10. aaronfrost commented on Jan 28, 2019

    @aaronfrost

    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

  11. LayZeeDK commented on Jan 28, 2019

    @LayZeeDK

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

  12. aaronfrost commented on Jan 28, 2019

    @aaronfrost

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

  13. ova2 commented on Jan 28, 2019

    @ova2

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

  14. iamrakesh commented on Jan 28, 2019

    @iamrakesh

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

  15. denisyilmaz commented on Jan 28, 2019

    @denisyilmaz

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

  16. iamrakesh commented on Jan 28, 2019

    @iamrakesh

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

  17. aaronfrost commented on Jan 28, 2019

    @aaronfrost

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

  18. ova2 commented on Jan 28, 2019

    @ova2

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

  19. aaronfrost commented on Jan 28, 2019

    @aaronfrost

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

  20. nasreddineskandrani commented on Jan 30, 2019

    @nasreddineskandrani

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

  21. filipesilva commented on Oct 9, 2019

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

  22. angular-automatic-lock-bot commented on Nov 9, 2019

    @angular-automatic-lock-bot

    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.

  23. locked and limited conversation to collaborators on Nov 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions