This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Lazy Load Angular 2 Modules with the Router

5:15 Angular 2 lesson by

Angular 2 lazy loading is a core feature of Angular 2. Lazy loading allows your application to start up faster because it only needs to use the main App Module when the page initially loads. As you navigate between routes, it will load the additional modules as you define them in your routes. Make sure to properly organize your files into Angular 2’s module pattern so that files that belong to each module don’t get required by other modules.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

Angular 2 lazy loading is a core feature of Angular 2. Lazy loading allows your application to start up faster because it only needs to use the main App Module when the page initially loads. As you navigate between routes, it will load the additional modules as you define them in your routes. Make sure to properly organize your files into Angular 2’s module pattern so that files that belong to each module don’t get required by other modules.

Avatar
Mac

{path: '', loadChildren: 'app/home/home.module'}

When using webpack, this results in an error: module app/home/home.module not found.
Anyone knows how to get this working with webpack?

Avatar
Spencer

I'm also using webpack - and am getting a "Uncaught (in promise): ReferenceError: System is not defined" error with this configuration. It only started once I began refactoring using the Lazy Load method above.

I understand this tutorial is using System.js - does the loadChildren config require System.js ??

Avatar
John

System.js is not a requirement, just the default. Webpack loaders are available such as https://github.com/Quramy/angular2-load-children-loader

In reply to Spencer
Avatar
John

I believe there's currently a bug/limitation around using default exports.

So you would target the @NgModule with loadChildren:'app/home/home.module#HomeModule' (where HomeModule is the exported class name of the module).

In reply to Mac
Avatar
Noah Rawlins

I can't get this to work using webpack based v17 of the angular-cli and 2.1 of angular 2. It tells me it can't find the home module.

edit: i got it to work. apparently with cli version 17 you need to use relative paths. so './home/home.component#HomeComponent'

In reply to John
Avatar
Jon

Tried your solution and it still doesn't work for me using webpack (in the angular CLI).

In reply to Noah Rawlins
Avatar
Charles

Same here, and solved here:

http://stackoverflow.com/questions/39493782/lazy-loading-in-angular2-rc7-and-angular-cli-webpack

Check my comment since i had a slighly different workaround.

Avatar
qemweb

I almost tried everything and still the same issue:
error_handler.js:50 EXCEPTION: Uncaught (in promise): Error: Cannot find module 'app/home/home.module' .
you can see more details about my issue here : http://stackoverflow.com/questions/41396839/exception-uncaught-in-promise-error-cannot-find-module-app-home-home-modul

Please can someone help me !!

Avatar
qemweb

it turns out that it's working on plunker but not on my machine !!!!!!!
https://plnkr.co/edit/RjhjnWMBeC3xyPAPmh7c?p=preview someone to help please !!!

In reply to qemweb

At this point, you probably think it's weird that my declarations inside of my app.module.ts, I need to declare the context and the HomeComponent. And then in my routes, I need to declare them as well. That seems like a lot of boilerplate and hard-coded dependencies between the router and components.

What you can actually do about that is, in...I'll take my "home" Directory. I'm going to create a Home Module. These modules are going to allow me to lazy load these packages or modules, or bundles of components and files, so we'll put another NgModule here.

home/home.module.ts

import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";

@NgModule({

})

This one will have imports:[CommonModule],. CommonModule has ng-if and all the common template things you'll use. It will have a declarations: [HomeComponent]. You remember how we just did that before in our app.module. Then we can export default class HomeModule.

home/home.module.ts

import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";

@NgModule({
  imports:[CommonModule],
  declarations: [HomeModule]
})
export default class HomeModule{}

What we do in here is in our imports we use that same RouterModule. This time instead of forRoot we use forChild. We'll configure the routes here, so routes. We say this time, the path. The root path of this module will load the component, HomeComponent. Drop these routes in here.

home/home.module.ts

import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";

const routes = [
  {path: '', component: HomeComponent}
];

@NgModule({
  imports:[CommonModule, RouterModule.forChild(routes)],
  declarations: [HomeModule]
})
export default class HomeModule{}

Then in my app.routes instead of saying component, I can say loadChildren. And loadChildren takes a string which will completely decouple these. The string will be app/home/home.module which is basically pointing to this app/home/home.module.

app/app.routes.ts

import {RouterModule} from "@angular/router";
import {ContactsComponent} from "./contacts/contacts.component.ts";
const routes = [
 {path : '', loadChildren: 'app/home/home.module'},
 {path: 'contacts', component:ContactsComponent}
];

export default RouterModule.forRoot(routes);

Finally in my app.module I can get rid of this HomeComponent reference, get rid of the import. I'll hit Save,

app/app.module.ts

import {AppComponent} from "./app.component";
import {NgModule} from "@angular/core";
import appRoutes from "./app.routes";
import {ContactsComponent} from "./contacts/contacts.component";

@NgModule({
    imports:[BrowserModule, appRoutes],
    declarations:[AppComponent, ContactsComponent],
    bootstrap:[AppComponent]
})

and now this will still load the HomeComponent. It's just that now we've decoupled this home and everything inside of it from the application.

I can do the same thing in my contacts component. Let's extract those routes out just like we did before. We'll say home.routes.ts. In my module I define my routes, so I'll paste those in here.

home/home.routes.ts

const routes = [
  {path: '', component: HomeComponent}
];

I'll grab this RouterModule.forChild, export that as the default. I'll make sure it'll import everything.

home/home.routes.ts

import {RouterModule} from "@angular/router";
import {HomeComponent} from "./home.component";
const routes = [
  {path: '', component: HomeComponent}
];

export default RouterModule.forChild(routes);

Then in my HomeModule I can get rid of that RouterModule, just import homeRoutes from "./home.routes". Now I'll drop my homeRoutes in here. That was just for organization purposes, and everything will still work the same.

home/home.module.ts

import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {HomeComponent} from "./home.component";
import homeRoutes from "./home.routes";

@NgModule({
  imports:[CommonModule, homeRoutes],
  declarations: [HomeModule]
})
export default class HomeModule{}

Now, let's do that same thing for contacts, real fast. We'll say contacts.module.ts. We'll create the NgModule. We'll import the CommonModule. We'll declare my contacts component, and export a default class of contacts module.

contacts/contacts.module.ts

import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {ContactsComponent} from "./contacts.component";
@NgModule({
  imports:[CommonModule],
  declarations: [ContactsComponent]
})
export default class ContactsModule

We'll define our routes in a file. We'll say contacts.routes ts. This will be routes, which is an array. This one will have the path at the root again, with a component of ContactsComponent. Then we'll export a default RouterModule.forChild(routes);.

contacts/contacts.routes.ts

import {ContactsComponent} from "./contacts.component";
import {RouterModule} from "@angular/router";
const routes = [
  {path: '', component: ContactsComponent}
];

export default RouterModule.forChild(routes)

In my contacts.module I can import the contacts routes from contactsRoutes, and drop them in my imports because this is a full module to import and load in.

contacts/contacts.module.ts

import contactsRoutes from "./contacts.routes"

@NgModule({
  imports:[CommonModule, contactsRoutes],
  declarations: [ContactsComponent]
})

Finally, in my app.routes I can say, loadChildren: 'app/contacts/contacts.module'. This is still at the 'contacts' path, that's fine.

app/app.routes.ts

const routes = [
 {path : '', loadChildren: 'app/home/home.module'},
 {path: 'contacts', loadChildren: 'app/contacts/contacts.module'}
];

In my app.module I can get rid of declaring the ContactsComponent, remove the import.

I'll hit Save, and now you'll see "I'm the home component", and then it's /contacts. You'll see the load, "this is a contacts component". We've decoupled these modules and inside the router we can remove these imports as well. Inside my route configuration for the app, you can see I'm loading in other modules.

app/app.routes.ts

const routes = [
 {path : '', loadChildren: 'app/home/home.module'},
 {path: 'contacts', loadChildren: 'app/contacts/contacts.module'}
];

I'm lazy loading them in. This is doing this at run time, so that your final builds are as small as possible. Your code is split across these different modules. The angular compiler is set to split and organize these modules just as you would expect, so you don't have to worry about that too much.

This loadChildren that you see here is configured in whatever you're using for a loader. But basically, the angular CLI or angular2 starter, or the basic system JS build will all pre-configure this for you.

Based on your custom settings, this path might change, so just check your loader and your build configuration on how that's set up. Again, you'll find no references to home or contacts inside of your app.module, or app.component. Everything is defined inside of app.routes where it's just a string pointing to the module it's going to load.

These modules are self-contained, and have their own route definitions based on the component it's going to use at this path.

home/home.modules.ts

const routes = [
  {path: '', component: HomeComponent}
];

Similarly with contacts, we'll see the path is nothing but this is really app routes contacts and then nothing. That's how when we load contacts, it will still load in that module, and load in this contacts component.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?