Open JeffreyDevloo opened 5 years ago
🤔 strange. I'm going to take a look at this tomorrow. Because I'm using this approach of lazy loading modules as part of another library which I'm also using in production. And it works with ng 8. I'll let you know
Very strange. I was able to reproduce it as you said on this repo. I've checked and it definitely isn't an issue with Ivy. There it works without problems.
However, as I mentioned, I'm using this approach of lazy loading modules in another bigger library, and there it works 🤔. I'm currently heading to ngrome.io to give a talk, so won't have too much time investigating probably. I'll definitely give it a closer look however next week.
Meanwhile you can have a look at the library yourself (maybe you can figure out the difference 😉 ). Just go to this repo https://github.com/juristr/ngx-lazy-el. It's an NX workspace, so theres the library in the libs
folder that gets published to NPM and the demo application which you can launch with yarn start
(or ng serve
).
You could also try to install that ngx-lazy-el
library via npm i @juristr/ngx-lazy-el@1.0.0-rc.1
and see whether that works in your project.
Is the AoT flag set in your bigger project?
I've taken a look at ngx-lazy-el
and it should encounter similar problems. It's using the compiler at runtime to compile a module and retrieve its component factory, similar to what you are doing here and what I am doing in my own project.
I'd say the answer lies in the magical naming convention required to ensure @ngtools/webpack creates a factory file for your module. See for reference: https://github.com/juristr/ngx-lazy-el/blob/d113ca2562bd3b34dbdd386dc00a58358847a478/libs/ngx-lazy-el/src/lib/tokens.ts
Any object with a property named loadChildren with a dynamic import to a module will trigger the AOT compiler to generate the factory file needed.
The reason you get Error: Runtime compiler is not loaded is that the module fails the if check during AOT mode, since it hasn't generated a factory file.
if (elementModuleOrFactory instanceof NgModuleFactory) {
// if ViewEngine (AOT has prebuilt it)
return elementModuleOrFactory;
}
Thanks @antur84.
The key lies in both the naming of a property 'loadChildren'. The callback has to include (m) => m.SOME_EXPORT
.
If these conditions are met, the module gets compiled with AoT and the factory file is present.
I've taken your response and tagged it as my answer on Stackoverflow with a link to your comment.
EDIT: Added response comment
@antur84 ohhhh very good catch 👏. In fact, in my ngx-lazy-load library I've used the router structure for the configuration to have it work even before v8. So in v7 I "simulated" a routing config, passing in the (at that time) magic lazy loading string and thus the CLI would split it out into a separate module etc..
Very interesting, so although we have dynamic imports there's still some magic involved. I'll update my blog post and definitely bring this to the table of the Angular team, as it is really misleading. Maybe we can add some feature/fix to have the factory file properly generated. I'll update you on how that goes
Do you know any workaround for this? We are trying to migrate our app from v7 to v8 and struggling with this issue. We have some non-routable lazy-loaded module which don't load anymore because of the missing NgModuleFactory...
I chose the simple workaround to have my own "lazy.service.ts" load
method accept a parameter in the shape of {loadChildren: () => Promise<any>}
.
Then I keep a (growing :)) file where I have predefined exports setup, one for each lazy component.
export const lazyComp1 = { loadChildren: () => import('path/to/module/name.module').then(x => x.LazyCompModule)};
when loading a component from some place, like on click of a button, I reference this exported property and call my lazy-service. the AOT compiler runs once at compile time and picks this pattern up properly.
import { lazyComp1} from '../lazy-components-registry';
this.lazyLoadService.load(lazyComp1).then(x ....
@antur84 @patachon @JeffreyDevloo Hey guys, I just tried to upgrade the project to Angular RC with Ivy enabled and it seems as if the issue has been resolved there. Can you verify that on your side as well?
Also, if you'd like to verify this on your project, upgrade to the RC and help find issues that might need to be resolved before v9 goes live. If you're wondering, here are the upgrade guides: https://next.angular.io/guide/updating-to-version-9. I'd be curious what the experience is for you
Have you tried adding emitDecoratorMetadata flag to complier options?
Serving with --prod breaks dynamic rendering
Serving the application with
ng serve --prod
breaks the dynamic rendering It's not the first nor the last tutorial with dynamic loading which is facing this issue.Reproduction
Steps to reproduce the problem:
npm install
ng serve --prod
localhost:4200
The console logs the following error:
Attempted solution
Ok. The error indicates that there is no compiler provided. Let's provide the JITCompiler to our application: Changing
app.module.ts
toWe should be using the JITCompiler to compile our modules.
Problem with this solution
Now when clicking the button:
Any help is appreciated, I am in the same boat as you are. See https://stackoverflow.com/questions/58220367/angular-8-lazy-loading-yourself-aot-no-ngmodule-metada