6
votes

Building an angular5 app. The app requires a few alternate layouts.

My approach is to handle the high level routing in the main app module routing file. The said file will map routes to modules. The modules will be lazy loaded for the non-essential speed driven pages and not lazy loaded for the speed critical pages:

app-layout-router.module.ts file:

import {NgModule} from '@angular/core';
import {PreloadAllModules, RouterModule, Routes} from '@angular/router';
import {PublicLayoutModule} from '@modules/layouts/public/public-layout.module';
import {AuthenticatedLayoutModule} from '@modules/layouts/authenticated/authenticated-layout.module';

const routes: Routes = [{
    path: '',
    loadChildren: () => PublicLayoutModule,
},{
    path: 'dashboard',
    loadChildren: () => AuthenticatedLayoutModule,
}];

@NgModule({
    imports: [RouterModule.forRoot(
        routes,
        {
            useHash: false,
            preloadingStrategy: PreloadAllModules
        }
    )],
    exports: [RouterModule],
})

export class AppLayoutRouter {
}

All routes not under dashboard will be pre-rendered for SEO reasons. In the authenticated layout however, I have the option to break the app down by lazy loading the subsequent modules. For example this is the content of the authenticated module router file:

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {AuthenticatedLayoutComponent} from './authenticated-layout.component';

const routes: Routes = [{
    path: '',
    component: AuthenticatedLayoutComponent,
    children: [{
        path: '',
        loadChildren: '../../dashboard/dashboard.module#DashboardModule'
    }]
}];

@NgModule({
    imports: [RouterModule.forChild(
        routes
    )],
    exports: [RouterModule],
})

export class AuthenticatedLayoutRoutingModule {
}

In theory this works, but in practice I hit errors. The expected result is the angular cli webpack will create a chunk for the dashboard module and the app will fetch it via lazy loading. In reality the result is the router cannot load the dashboard module as it thinks the path to the module is not correct.

Here is the error thrown:

Error: Cannot find module '../../dashboard/dashboard.module'.
    at eval (eval at ../../../../../src/$$_lazy_route_resource lazy recursive (main.bundle.js:6), <anonymous>:5:9)
    at ZoneDelegate.invoke (zone.js:388)
    at Object.onInvoke (core.js:4733)
    at ZoneDelegate.invoke (zone.js:387)
    at Zone.run (zone.js:138)
    at eval (zone.js:858)
    at ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.js:4724)
    at ZoneDelegate.invokeTask (zone.js:420)
    at Zone.runTask (zone.js:188)
    at eval (eval at ../../../../../src/$$_lazy_route_resource lazy recursive (main.bundle.js:6), <anonymous>:5:9)
    at ZoneDelegate.invoke (zone.js:388)
    at Object.onInvoke (core.js:4733)
    at ZoneDelegate.invoke (zone.js:387)
    at Zone.run (zone.js:138)
    at eval (zone.js:858)
    at ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.js:4724)
    at ZoneDelegate.invokeTask (zone.js:420)
    at Zone.runTask (zone.js:188)
    at resolvePromise (zone.js:809)
    at resolvePromise (zone.js:775)
    at eval (zone.js:858)
    at ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.js:4724)
    at ZoneDelegate.invokeTask (zone.js:420)
    at Zone.runTask (zone.js:188)
    at drainMicroTaskQueue (zone.js:595)
    at ZoneTask.invokeTask [as invoke] (zone.js:500)
    at invokeTask (zone.js:1517)
defaultErrorLogger @ core.js:1440
ErrorHandler.handleError @ core.js:1501
next @ core.js:5481
schedulerFn @ core.js:4319
SafeSubscriber.__tryOrUnsub @ Subscriber.js:240
SafeSubscriber.next @ Subscriber.js:187
Subscriber._next @ Subscriber.js:128
Subscriber.next @ Subscriber.js:92
Subject.next @ Subject.js:56
EventEmitter.emit @ core.js:4299
(anonymous) @ core.js:4755
ZoneDelegate.invoke @ zone.js:388
Zone.run @ zone.js:138
NgZone.runOutsideAngular @ core.js:4681
onHandleError @ core.js:4755
ZoneDelegate.handleError @ zone.js:392
Zone.runGuarded @ zone.js:154
_loop_1 @ zone.js:684
api.microtaskDrainDone @ zone.js:693
drainMicroTaskQueue @ zone.js:602
ZoneTask.invokeTask @ zone.js:500
invokeTask @ zone.js:1517
globalZoneAwareCallback @ zone.js:1543

I can fix this by setting all the module routing to lazy loading (ie lazy load everything)... but I do not want to do this for the public facing pages, eg the login page. I can pre-render the pages just fine, the user will get a pre-rendered index.html file for the login.. but cannot interact with the page till the router has fetched the module from the back-end via lazy loading...

Has anyone else solved this problem? And if so how?

Here is the code in question that fails: https://github.com/jdcrecur/ang5ModuleRouting

1
What kind of error are you getting? Can you please post the error. Tnxjoel
Error added to the postJohn
If you set the loadChildren path relative to the app root such as loadChildren: 'app/dashboard/dashboard.module#DashboardModule', does that change anything?Alexander Staroselsky
Same error but with the new path: core.js:1440 ERROR Error: Uncaught (in promise): Error: Cannot find module 'app/modules/page/dashboard/dashboard.module'.John
I have posted the code to github if anyone is interested: github.com/jdcrecur/ang5ModuleRoutingJohn

1 Answers

3
votes

I'm experiencing the same issue. This could be a bug with the angular-cli. I'm still not sure whether the bug is in the cli or in our code!

As mentioned by Gerrit on the other question here it's still possible to compile with aot: ng serve --aot

I've also resolved the issue by downgrading my angular-cli as mentioned on github from 1.7.2 to 1.6.8, which is the last CLI-Version which seems to work in our case.