Angular fails routing to lazy loaded modules after deploy

Viewed 3984

I've recently published a new Angular app, which implements lazy loading.

Now, app compilation goes well, no error is show and running it in local environment for debug or using ng serve (or ng serve --prod to emulate the behaviour while in production mode) the app works fine and I can succesfully navigate from an eager loaded module to a lazy loaded one and vice versa, without errors.


THE ISSUE:

If I run the app in the server (so using ng build --prod and then publishing it), I can't navigate from the first default eager loaded module (the homepage) to a lazy loaded module.


ERROR STACK

The errors I get in the console are the following:

  1. error:

    ERROR Error: Uncaught (in promise): Error: Cannot enable prod mode after platform setup. Error: Cannot enable prod mode after platform setup.

    displayed when… trying to navigate to any lazy loaded module, at the first time

  2. error:

    ERROR Error: Uncaught (in promise): Error: Cannot find 'LazyModule' in './lazy/lazy.module' Error: Cannot find 'LazyModule' in './lazy/lazy.module'

    displayed when… I try to navigate to a lazy loaded component again, after the first error showed up

An interesting fact is that when displaying these errors (expecially the second), it doesn't throw an error 404 not found, it's just a "normal" Angular error without any 404 status.


MY CODE:

project structure

app.module.ts (& app.routing.ts)
|__lazy.module.ts (& lazymodule.routing.ts)
|__admin.module.ts (& admin.routing.ts)

app.routing.ts

// ... imports …
const appRoutes: Routes = [
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full',
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'lazy',     
    loadChildren: "./lazy/lazy.module#LazyModule",
    canLoad: [AuthGuard]
  },
  {
    path: 'admin',     
    loadChildren: "./admin/admin.module#AdminModule",
    canLoad: [AuthGuard]
  },
  { path: '**', redirectTo: '/dashboard', canActivate: [AuthGuard] }
];

export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes, { preloadingStrategy: SelectiveStrategy, useHash:true });

selective.strategy.ts

// ... imports ...
@Injectable()
export class SelectiveStrategy implements PreloadingStrategy {

    preload(route: Route, load: Function): Observable<any> {
        if (route.data && route.data['preload']) {
            return load();
        }
        return Observable.of(null);
    }
}

lazymodule.routing.ts

const routes: Routes = [
  { path: '', component: FooComponent, canActivate: [AuthGuard] },
  {
    path: 'foo',
    component: FooComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'new',
    component: NewElementComponent,
    canActivate: [AuthGuard]
  },
  { path: '**', redirectTo: '/dashboard', canActivate: [AuthGuard] }
];

export const routing: ModuleWithProviders = RouterModule.forChild(routes);

VERSIONS:

server Windows Server 2012 R2
IIS 8.5.9600.16384
angular/common 6.1.5
angular & angular-cli 6.1.4


MY TRIES:

1) Tried to execute locally the ng build --prod generated files. It works.
2) Tried to execute the app (locally) with ng serve --prod, but nothing seems to work
3) Tried to add useHash:true option as you can see in the RouterModule.forRoot of app.routing.ts file above. Didn't work.
4) Tried to move the routing inside the app.module.ts file. Didn't work.
5) Tried to use a root relative path as src/app/lazy/lazy.module#LazyModule. Didn't work.
6) Tried to copy in the server folder the files generated from ng serve --prod, but nothing.
7) Tried to update all of my npm packages, but nothing.


What am I doing wrong? Is it a code issue or a server one?

Let me knwo if you need more infos/code sections

2 Answers

I've found a temporary solution. Unfortunately it seems a permanent fix is something the angular team will have to take care of as it is an issue with the production build aot and build-optimizer.

For now, you have to disable aot and build-optimization. You can do this in one of two places either on the command line for a one time effect:

ng build --prod --aot=false --build-optimizer=false

Or for a permanent effect in your angular.json, change your production configs to:

"configurations": {
        "production": {
          "optimization": true,
          "outputHashing": "all",
          "sourceMap": false,
          "extractCss": true,
          "namedChunks": false,
          "aot": false,
          "extractLicenses": true,
          "vendorChunk": false,
          "buildOptimizer": false,
          "fileReplacements": [
            {
              "replace": "src/environments/environment.ts",
              "with": "src/environments/environment.prod.ts"
            }
          ]
        }
      }

note the false for aot and build-optimizer

use fat arror function for lazyLoading, so there no need to worry about module path

here's is an example

const appRoutes: Routes = [
  {
    path: '',
    redirectTo: '/dashboard',
    pathMatch: 'full',
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'lazy',     
    loadChildren: () => LazyModule,
    canLoad: [AuthGuard]
  },
  {
    path: 'admin',     
    loadChildren: () => AdminModule,
    canLoad: [AuthGuard]
  },
  { path: '**', redirectTo: '/dashboard', canActivate: [AuthGuard] }
];
Related