Skip to content

Router fails to synchronously load NgModule with "Runtime compiler is not loaded" error #18061

Closed

Description

@antonmoiseev

I'm submitting a ...


[ ] Regression (behavior that used to work and stopped working in a new release)
[x] Bug report 
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

Router fails with "Runtime compiler is not loaded" error while attempting to synchronously load an NgModule in production mode.

Expected behavior

The module is successfully loaded by the router.

Minimal reproduction of the problem with instructions

Since the issue is reproducible only in the production mode, it cannot be demoed with a plunker. Here is a minimal app generated with Angular CLI that reproduces the issue - https://github.com/antonmoiseev/router-issue.

Steps to reproduce:

  1. Generate an app with Angular CLI:
    ng new router-issue --minimal true
  2. Generate a new module with a single component:
    ng g m home --spec false && ng g c home --spec false -it -is
  3. Add routing configuration for the HomeModule:
    @NgModule({
      imports: [
    +   RouterModule.forChild([
    +     { path: '', component: HomeComponent }
    +   ])
      ],
      declarations: [ HomeComponent ]
    })
    export class HomeModule {}
  4. Add routing configuration for the AppModule:
    +export function homeModuleLoader() {
    +  return HomeModule;
    +}
    
    @NgModule({
      imports: [
        BrowserModule,
    +   RouterModule.forRoot([
    +     { path: '', loadChildren: homeModuleLoader }
    +   ])
      ],
      declarations: [ AppComponent ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule {}
  5. Add router outlet to the AppComponent:
    @Component({
      selector: 'app-root',
      template: `
    +   <h1>App component</h1>
    +   <router-outlet></router-outlet>
      `
    })
    export class AppComponent {}
  6. Start the development web server in the production mode:
    ng serve --prod -o
  7. Open dev tools in the browser, you should see following error in the console:
    image

What is the motivation / use case for changing the behavior?

I prefer creating a separate NgModule per page to better scope declarations / providers which helps maintaining the app. At the same time it makes sense to load some pages along with the main bundle (hence synchronous module loading in the router config) since users immediately face them anyway (e.g. Home page, Login page). Currently it's not possible.

Please tell us about your environment


Angular version: 4.2.6

@angular/cli: 1.2.0
node: 8.1.3
os: darwin x64
@angular/animations: 4.2.6
@angular/common: 4.2.6
@angular/compiler: 4.2.6
@angular/core: 4.2.6
@angular/forms: 4.2.6
@angular/http: 4.2.6
@angular/platform-browser: 4.2.6
@angular/platform-browser-dynamic: 4.2.6
@angular/router: 4.2.6
@angular/cli: 1.2.0
@angular/compiler-cli: 4.2.6
@angular/language-service: 4.2.6

Browser:
- [x] Chrome (desktop) version 59.0.3071.115
- [x] Firefox version 54.0.1 (64-bit)
- [x] Safari (desktop) version 10.1.1 (11603.2.5)
 
For Tooling issues:
- Node version: v8.1.3 
- Platform: Mac 

Others:

Activity

  1. Toxicable commented on Jul 12, 2017

    @Toxicable

    Since you never reference the RuntimeCompiler it is tree shaked from the bundle by the CLI.
    I don't know what this is:

    export function homeModuleLoader() {
      return HomeModule;
    }

    but this is how you lazy load routes: https://angular.io/guide/router#lazy-loading-route-configuration

    {
      path: 'admin',
      loadChildren: 'app/admin/admin.module#AdminModule',
    },
  2. antonmoiseev commented on Jul 12, 2017

    @antonmoiseev
    ContributorAuthor

    @Toxicable I want the HomeModule to be inlined into main bundle, hence I don't use lazy loading in this app. homeModuleLoader is the way to synchronously load the module and make it compatible with AoT, you can read about it here - #13909.

  3. jasonaden commented on Jul 17, 2017

    @jasonaden
    Contributor

    @antonmoiseev You're right, but you actually need to return the NgFactory from your homeModuleLoader function. So it would be more like this:

    +export function homeModuleLoader() {
    +  return HomeModuleNgFactory;
    +}
    
    @NgModule({
      imports: [
        BrowserModule,
    +   RouterModule.forRoot([
    +     { path: '', loadChildren: homeModuleLoader }
    +   ])
      ],
      declarations: [ AppComponent ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule {}
    

    If you look at #13909 (and the issue linked to it), you'll see the fix was to make this work, but you still need to return the NgFactory. Otherwise you would need to send down the RuntimeCompiler, which is quite large and I doubt you want to do so in a production app.

  4. yfain commented on Aug 21, 2017

    @yfain

    The AoT compiler was giving me "ERROR in Error encountered resolving symbol values statically. Function calls are not supported. Consider replacing the function or lambda with a reference to an exported function" while trying to do something like this:

    {path: 'shipping', loadChildren:  () => ShippingModule}
    

    After using the workaround suggested by @antonmoiseev the AoT went fine:

    export function shippingModuleLoader() { 
      return ShippingModule;
    }
    ...
     {path: 'shipping', loadChildren: shippingModuleLoader}
    
  5. antonmoiseev commented on Aug 21, 2017

    @antonmoiseev
    ContributorAuthor

    @jasonaden I believe CLI was replacing HomeModule with HomeModuleNgFactory at some point, since the factory becomes available only after ngc compiles the source code. Do you suggest it might be the CLI issue?

  6. worldspawn commented on Jan 12, 2018

    @worldspawn

    So @jasonaden you havent explained how to get a reference to this magic factory?

    The really frustrating thing here is all I (and the op) wanted to do was mount a submodule to a specific path and all this seemingly unrelated rubbish has come in to play, as for some reason mounting modules to a path and lazy loading has been horrible intertwined.

  7. glebmachine commented on May 6, 2018

    @glebmachine

    Got this error with angular 6

  8. Toxicable commented on May 6, 2018

    @Toxicable

    @glebmachine could you provide a minimal production of the issue?

  9. onderceylan commented on Jul 25, 2019

    @onderceylan

    In order to eagerly load modules with a function reference when AOT enabled for Angular 7, you need to create an NgFactory module as you can see from this PR - #13909.

    A working solution for me was creating a clone of my module and adding .ngfactory postfix to the filename and also adding NgFactory postfix to the module class name.

    import { FirstPageModuleNgFactory } from '../pages/first-page.module.ngfactory';
    import { SecondPageModuleNgFactory } from '../pages/second-page.module.ngfactory';
    
    export function loadFirstPageModule() {
        return FirstPageModuleNgFactory;
    }
    
    export function loadSecondPageModule() {
        return SecondPageModuleNgFactory;
    }
    
    const routes: Routes = [
        {
            path: '',
            component: HomePageComponent,
            children: [
                {
                    path: '',
                    redirectTo: 'first',
                    pathMatch: 'full',
                },
                {
                    path: 'first',
                    loadChildren: loadFirstPageModule,
                },
                {
                    path: 'second',
                    loadChildren: loadSecondPageModule,
                },
            ],
        },
    ];
  10. angular-automatic-lock-bot commented on Sep 15, 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.

  11. locked and limited conversation to collaborators on Sep 15, 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

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions