9

Hi I'm trying to use angular-cli with webpack (+productivity) to build my angular2 app but I'm having issues when trying to lazy load modules which were working with version beta.10...

Project structure:

Project structure

package.json

{
  "name": "my-app",
  "version": "0.0.0",
  "license": "MIT",
  "angular-cli": {},
  "scripts": {
    "start": "ng serve",
    "lint": "tslint \"src/**/*.ts\"",
    "test": "ng test",
    "pree2e": "webdriver-manager update",
    "e2e": "protractor"
  },
 "private": true,
 "dependencies": {
    "@angular/common": "2.0.0-rc.7",
    "@angular/compiler": "2.0.0-rc.7",
    "@angular/core": "2.0.0-rc.7",
    "@angular/forms": "^2.0.0-rc.7",
    "@angular/http": "2.0.0-rc.7",
    "@angular/platform-browser": "2.0.0-rc.7",
    "@angular/platform-browser-dynamic": "2.0.0-rc.7",
    "@angular/router": "3.0.0-rc.2",
    "angular2-cookie": "1.2.3",
    "core-js": "2.4.0",
    "material-design-icons": "2.2.3",
    "material-design-lite": "1.2.0",
    "reflect-metadata": "0.1.3",
    "rxjs": "5.0.0-beta.12",
    "ts-helpers": "^1.1.1",
    "zone.js": "0.6.21"
  },
  "devDependencies": {
    "@types/jasmine": "2.2.30",
    "@types/protractor": "1.5.16",
    "angular-cli": "^1.0.0-beta.11-webpack.9-4",
    "codelyzer": "0.0.26",
    "jasmine-core": "2.4.1",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "0.13.22",
    "karma-chrome-launcher": "0.2.3",
    "karma-coverage": "1.0.0",
    "karma-jasmine": "0.3.8",
    "protractor": "3.3.0",
    "ts-node": "1.2.1",
    "tslint": "3.13.0",
    "typescript": "2.0.0"
  }
}

angular-cli.json

{
  "project": {
    "version": "1.0.0-beta.11-webpack",
    "name": "my-app"
  },
  "apps": [
    {
      "main": "main.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.json",
      "prefix": "app",
      "index": "index.html",
      "root": "src",
      "mobile": false,
      "scripts": [
        "polyfills.ts",
        "../node_modules/material-design-lite/material.min.js"
      ],
      "styles": [
        "../node_modules/material-design-icons/iconfont/material-icons.css",
        "../node_modules/material-design-lite/material.css"
      ],
      "assets": "assets",
      "environments": {
        "source": "environments/environment.ts",
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "addons": [],
  "packages": [],
  "e2e": {
    "protractor": {
      "config": "config/protractor.conf.js"
    }
  },
  "test": {
    "karma": {
      "config": "config/karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "scss",
    "prefixInterfaces": false
  }
}

tsconfig.json

{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [
      "es6",
      "dom"
    ],
    "mapRoot": "./",
    "module": "es6",
    "moduleResolution": "node",
    "outDir": "../dist/out-tsc",
    "sourceMap": true,
    "target": "es5",
    "typeRoots": [
      "../node_modules/@types"
    ],
    "types": [
      "jasmine"
    ]
  }
}

app.routes.ts

import {Routes, RouterModule} from "@angular/router";
import {PageNotFoundComponent} from "./404.component";
import {AuthenticationGuard} from "./base/security";

const routes: Routes = [
  { path: '', redirectTo: 'home', pathMatch: 'full', canActivate: [AuthenticationGuard] },
  { path: 'home', loadChildren: 'app/modules/home/home.module#HomeModule' },
  { path: '**', component: PageNotFoundComponent }
];

export const routing = RouterModule.forRoot(routes, { useHash: true });

app.module.ts

import {NgModule} from "@angular/core";
import {BrowserModule} from "@angular/platform-browser";
import {routing} from "./app.routes";
import {AppComponent} from "./app.component";
import {PageNotFoundComponent} from "./404.component";
import {CoreModule} from "./core";

@NgModule({
  imports: [
    BrowserModule,
    routing,
    CoreModule
  ],
  declarations: [
    AppComponent,
    PageNotFoundComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

modules/home/home.routes.ts

import {RouterModule} from '@angular/router';
import {HomeComponent} from './home.component';

import {AuthenticationGuard} from '../../base/security';

export const routing = RouterModule.forChild([
  { path: '', component: HomeComponent, canActivate: [AuthenticationGuard] }
]);

modules/home/home.module.ts

import {NgModule} from '@angular/core';
import {BaseModule} from '../../base/base.module';
import {routing} from './home.routes';
import {HomeComponent} from './home.component';
import {NavigationMenuComponent} from '../../base/components';

@NgModule({
    imports: [
    BaseModule,
    routing
  ],
  declarations: [
    HomeComponent,
    NavigationMenuComponent
  ],
  exports: [],
  providers: []
})
export class HomeModule {}

console error message: Console Error Message

Is there anything I'm forgetting here? I wasn't able to find any documentation anywhere on how to proceed with this... Thanks in advance!

4
  • Project structure you provided doesn't contain folder home in folder app/modules, as a matter of fact, there's no file home.module at all, so when you go to route /home, there's no module to load: { path: 'home', loadChildren: 'app/modules/home/home.module#HomeModule' }. You can't load something that doesn't exist. (at least based on project structure picture you provided) Commented Sep 14, 2016 at 13:59
  • Its not being displayed because the folder isn't opened, but its there. I will update the image Commented Sep 14, 2016 at 14:12
  • Have you tried { path: 'home', loadChildren: 'src/app/modules/home/home.module#HomeModule' }? (I added src to path) Commented Sep 14, 2016 at 14:24
  • 1
    I'm getting the same error and have posted the same question here: stackoverflow.com/q/39493782/616304 No replies yet but nice to know I'm not the only one getting this :-( Commented Sep 14, 2016 at 15:40

4 Answers 4

5

There's a loader for this (angular2-router-loader). However you can't use it with the CLI without hacking the config. Luckily, es6-promise-loader does work with the CLI out-of-the-box.

This is what worked out for me:

First we'll need the es6-promise-loader:

npm i --save-dev es6-promise-loader

Then define your route like this:

{path:"lazy", loadChildren: () => require('es6-promise!./path/to/module')('ClassName')}

the es6-promise-loader replaces the above with this:

loadChildren: () => new Promise(function (resolve) {
        require.ensure([], function (require) {
          resolve(require('./path/to/module')['ClassName']));
        });
      });

which is the proper way to load a module with webpack, but cumbersome to put into every route.

Sign up to request clarification or add additional context in comments.

Comments

2

Angular-cli changed the build system between beta.10 and beta.14, from SystemJS to Webpack.

I tried using lazy loading with angular-cli(beta.17) and it worked for me.

As per my understanding,

SystemJs - we need to give full path for lazy loading Ex: ./app/dashboard/dashboard.module#DashboardModule

Webpack - we need to give relative path for lazy loading Ex: ./dashboard/dashboard.module#DashboardModule

Note : Don't forget to restart the server to see the changes reflecting.(ng serve)

This minor observation fixed my issue. Please see if this is useful or correct me on anything.

Comments

1

I have another solution :

on your app.routers.ts create a function like that :

let loadAoTModule = (path, module) => {

  let result = () => new Promise(function (resolve) {
    (require as any).ensure([], function (require: any) {
      resolve(require(path)[module]);
    });
  });

  return result;
}

const routes: Routes = [
  { path: '', redirectTo: 'customer', pathMatch: 'full'},

  { path          : 'customer'  ,
    // for a file locate on 'src/app/customer/customer.module'
    // and a module 'CustomerModule'
    loadChildren  :  loadAoTModule('./customer/customer.module', 'CustomerModule')

  }
];

work fine for me.

Comments

0

Use import to load the module dynamically. Remember to add "module": "esNext", in the tsconfig file.

loadChildren: () => import('projects/Reservation/src/app/app.module').then((m: any) => m.AppModule),

AppModule is the class name of the module.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.