Angular 2 routing submodules with AOT and Rollup
What's the correct way to set up a submodule in Angular 2 so that it works after AOT and rollup? I don't care about lazy loading and would be happy to have all submodules merged together, but loadChildren
is the cleanest way to reference a submodule and use it correctly <router-outlet>
, and although I've tried various methods, none will work either in development or production.
I followed the instructions in the AOT cookbook to prepare my application for deployment. My module structure is root> account> admin and I want admin routes to be loaded into the output defined by the account component.
Here's the router config for the account module:
import { NgModule } from '@angular/core';
import { AdminModule } from './admin/admin.module';
const accountRoutes: Routes = [{
path: 'account',
component: AccountComponent,
children: [
{ path: 'setup', component: SetupComponent },
{ path: 'admin', loadChildren: () => AdminModule }
]
}];
This works in development, but NGC compilation fails with Error: Error encountered resolving symbol values statically. Reference to a local (non-exported) symbol 'accountRoutes'.
I added export to accountRoutes
, but that also won't compile with Error: Error encountered resolving symbol values statically. Function calls are not supported.
.
One suggestion was to use a string instead of a function so that NGC can compile the code:
loadChildren: 'app/account/admin/admin.module#AdminModule'
This works in development and compilation, but the compiled application does not start because SystemJS is not available. Mistake:
ERROR Error: Uncaught (in promise): TypeError: System.import is not a function
TypeError: System.import is not a function
at t.loadFactory (build.js:6)
at t.load (build.js:6)
at t.loadModuleFactory (build.js:12)
I tried to include SystemJS in the build but couldn't find a separate module file app/account/admin/admin.module.ngfactory
- after collapsing everything in one build.js file. There might be a way to create separate drives for each submodule, but that's a lot of work.
I found a suggestion to reference the exported function:
export function loadAdminModule() {
return AdminModule;
}
loadChildren: loadAdminModule
This works in development, but in production the runtime compiler is not available, so it registers build.js:1 ERROR Error: Uncaught (in promise): Error: Runtime compiler is not loaded
.
The helper function can be changed, so it works in production by referencing NgFactory instead, but that won't work in development.
import { AdminModuleNgFactory } from '../../../aot/src/app/account/admin/admin.module.ngfactory';
export function loadAdminModule() {
return AdminModuleNgFactory;
}
loadChildren: loadAdminModule
Is there a supported use case loadChildren
to make it work with the instructions in the AOT cookbook? Is it better to use webpack?
source to share
For those having the same problem, here's a temporary workaround. Hopefully a better answer will come soon.
Now I am defining my submodules in a dedicated file submodules.ts
. I have two versions of this file, one submodules-jit.ts
for development and one submodules-aot.ts
for AOT / rollup deployment for production. I gitignore submodules.ts
and added commands to my scripts npm start
and npm build:aot
that are substituted for what I want .
// submodules-jit.ts
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModule; }
// submodules-aot.ts
import { AdminModuleNgFactory } from '../../aot/src/app/account/admin/admin.module.ngfactory'
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModuleNgFactory; }
export function adminModuleKeep(): any { return AdminModule; }
The admin module must be listed in the AOT file for it to be saved.
Then I use the function adminModule
in my routes:
import { adminModule } from '../submodules'
{ path: 'admin', loadChildren: adminModule }
Finally, for convenience, the npm scripts end up in the correct file.
"build:aot": "cp src/submodules-aot.ts src/app/submodules.ts && ngc -p tsconfig-aot.json && rollup -c rollup-config.js",
"start": "cp src/submodules-jit.ts src/app/submodules.ts && concurrently \"npm run build:watch\" \"npm run serve\""
Needless to say, this is a terrible hack, but it keeps the routing files clean and most of the evil in one place.
source to share
In my case, I solved it with your tip on loading routes from an external module and then read this issue on the angular / cli page .
So I figured out the refactoring of my routes from:
import { TestModule } from './pathToModule/test.module';
export function exportTestModule() {
return TestModule;
}
. . .
{
path: 'test',
loadChildren: exportTestModule
}
To:
. . .
{
path: 'test',
loadChildren: './pathToModule#TestModule'
}
source to share