{ "id": "guide/lazy-loading-ngmodules", "title": "Lazy-loading feature modules", "contents": "\n\n\n
\n mode_edit\n
\n\n\n
\n

Lazy-loading feature moduleslink

\n

By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading—a design pattern that loads NgModules as needed. Lazy loading helps keep initial\nbundle sizes smaller, which in turn helps decrease load times.

\n
\n

For the final sample app with two lazy-loaded modules that this page describes, see the\n.

\n
\n\n

Lazy loading basicslink

\n

This section introduces the basic procedure for configuring a lazy-loaded route.\nFor a step-by-step example, see the step-by-step setup section on this page.

\n

To lazy load Angular modules, use loadChildren (instead of component) in your AppRoutingModule routes configuration as follows.

\n\n\nconst routes: Routes = [\n {\n path: 'items',\n loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)\n }\n];\n\n\n

In the lazy-loaded module's routing module, add a route for the component.

\n\n\nconst routes: Routes = [\n {\n path: '',\n component: ItemsComponent\n }\n];\n\n\n

Also be sure to remove the ItemsModule from the AppModule.\nFor step-by-step instructions on lazy loading modules, continue with the following sections of this page.

\n\n

Step-by-step setuplink

\n

There are two main steps to setting up a lazy-loaded feature module:

\n
    \n
  1. Create the feature module with the CLI, using the --route flag.
  2. \n
  3. Configure the routes.
  4. \n
\n

Set up an applink

\n

If you don’t already have an app, you can follow the steps below to\ncreate one with the CLI. If you already have an app, skip to\nConfigure the routes. Enter the following command\nwhere customer-app is the name of your app:

\n\nng new customer-app --routing\n\n

This creates an app called customer-app and the --routing flag\ngenerates a file called app-routing.module.ts, which is one of\nthe files you need for setting up lazy loading for your feature module.\nNavigate into the project by issuing the command cd customer-app.

\n
\n

The --routing option requires Angular/CLI version 8.1 or higher.\nSee Keeping Up to Date.

\n
\n

Create a feature module with routinglink

\n

Next, you’ll need a feature module with a component to route to.\nTo make one, enter the following command in the terminal, where customers is the name of the feature module. The path for loading the customers feature modules is also customers because it is specified with the --route option:

\n\nng generate module customers --route customers --module app.module\n\n

This creates a customers folder having the new lazy-loadable feature module CustomersModule defined in the customers.module.ts file and the routing module CustomersRoutingModule defined in the customers-routing.module.ts file. The command automatically declares the CustomersComponent and imports CustomersRoutingModule inside the new feature module.

\n

Because the new module is meant to be lazy-loaded, the command does NOT add a reference to the new feature module in the application's root module file, app.module.ts.\nInstead, it adds the declared route, customers to the routes array declared in the module provided as the --module option.

\n\nconst routes: Routes = [\n {\n path: 'customers',\n loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)\n }\n];\n\n\n

Notice that the lazy-loading syntax uses loadChildren followed by a function that uses the browser's built-in import('...') syntax for dynamic imports.\nThe import path is the relative path to the module.

\n
\n
String-based lazy loading
\n

In Angular version 8, the string syntax for the loadChildren route specification was deprecated in favor of the import() syntax. However, you can opt into using string-based lazy loading (loadChildren: './path/to/module#Module') by including the lazy-loaded routes in your tsconfig file, which includes the lazy-loaded files in the compilation.

\n

By default the CLI will generate projects which stricter file inclusions intended to be used with the import() syntax.

\n
\n

Add another feature modulelink

\n

Use the same command to create a second lazy-loaded feature module with routing, along with its stub component.

\n\nng generate module orders --route orders --module app.module\n\n

This creates a new folder called orders containing the OrdersModule and OrdersRoutingModule, along with the new OrdersComponent source files.\nThe orders route, specified with the --route option, is added to the routes array inside the app-routing.module.ts file, using the lazy-loading syntax.

\n\nconst routes: Routes = [\n {\n path: 'customers',\n loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)\n },\n {\n path: 'orders',\n loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)\n }\n];\n\n\n

Set up the UIlink

\n

Though you can type the URL into the address bar, a navigation UI is easier for the user and more common.\nReplace the default placeholder markup in app.component.html with a custom nav\nso you can easily navigate to your modules in the browser:

\n\n<h1>\n {{title}}\n</h1>\n\n<button routerLink=\"/customers\">Customers</button>\n<button routerLink=\"/orders\">Orders</button>\n<button routerLink=\"\">Home</button>\n\n<router-outlet></router-outlet>\n\n\n\n

To see your app in the browser so far, enter the following command in the terminal window:

\n\nng serve\n\n

Then go to localhost:4200 where you should see “customer-app” and three buttons.

\n
\n \"three\n
\n

These buttons work, because the CLI automatically added the routes to the feature modules to the routes array in app.module.ts.

\n\n

Imports and route configurationlink

\n

The CLI automatically added each feature module to the routes map at the application level.\nFinish this off by adding the default route. In the app-routing.module.ts file, update the routes array with the following:

\n\nconst routes: Routes = [\n {\n path: 'customers',\n loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)\n },\n {\n path: 'orders',\n loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)\n },\n {\n path: '',\n redirectTo: '',\n pathMatch: 'full'\n }\n];\n\n\n

The first two paths are the routes to the CustomersModule and the OrdersModule.\nThe final entry defines a default route. The empty path matches everything that doesn't match an earlier path.

\n

Inside the feature modulelink

\n

Next, take a look at the customers.module.ts file. If you’re using the CLI and following the steps outlined in this page, you don’t have to do anything here.

\n\nimport { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { CustomersRoutingModule } from './customers-routing.module';\nimport { CustomersComponent } from './customers.component';\n\n@NgModule({\n imports: [\n CommonModule,\n CustomersRoutingModule\n ],\n declarations: [CustomersComponent]\n})\nexport class CustomersModule { }\n\n\n

The customers.module.ts file imports the customers-routing.module.ts and customers.component.ts files. CustomersRoutingModule is listed in the @NgModule imports array giving CustomersModule access to its own routing module. CustomersComponent is in the declarations array, which means CustomersComponent belongs to the CustomersModule.

\n

The app-routing.module.ts then imports the feature module, customers.module.ts using JavaScript's dynamic import.

\n

The feature-specific route definition file customers-routing.module.ts imports its own feature component defined in the customers.component.ts file, along with the other JavaScript import statements. It then maps the empty path to the CustomersComponent.

\n\nimport { NgModule } from '@angular/core';\nimport { Routes, RouterModule } from '@angular/router';\n\nimport { CustomersComponent } from './customers.component';\n\n\nconst routes: Routes = [\n {\n path: '',\n component: CustomersComponent\n }\n];\n\n@NgModule({\n imports: [RouterModule.forChild(routes)],\n exports: [RouterModule]\n})\nexport class CustomersRoutingModule { }\n\n\n

The path here is set to an empty string because the path in AppRoutingModule is already set to customers, so this route in the CustomersRoutingModule, is already within the customers context. Every route in this routing module is a child route.

\n

The other feature module's routing module is configured similarly.

\n\nimport { OrdersComponent } from './orders.component';\n\nconst routes: Routes = [\n {\n path: '',\n component: OrdersComponent\n }\n];\n\n\n

Verify lazy loadinglink

\n

You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing Cmd+Option+i on a Mac or Ctrl+Shift+j on a PC and go to the Network Tab.

\n
\n \"lazy\n
\n

Click on the Orders or Customers button. If you see a chunk appear, everything is wired up properly and the feature module is being lazy loaded. A chunk should appear for Orders and for Customers but will only appear once for each.

\n
\n \"lazy\n
\n

To see it again, or to test after working in the project, clear everything out by clicking the circle with a line through it in the upper left of the Network Tab:

\n
\n \"lazy\n
\n

Then reload with Cmd+r or Ctrl+r, depending on your platform.

\n

forRoot() and forChild()link

\n

You might have noticed that the CLI adds RouterModule.forRoot(routes) to the AppRoutingModule imports array.\nThis lets Angular know that the AppRoutingModule is a routing module and forRoot() specifies that this is the root routing module.\nIt configures all the routes you pass to it, gives you access to the router directives, and registers the Router service.\nUse forRoot() only once in the application, inside the AppRoutingModule.

\n

The CLI also adds RouterModule.forChild(routes) to feature routing modules.\nThis way, Angular knows that the route list is only responsible for providing additional routes and is intended for feature modules.\nYou can use forChild() in multiple modules.

\n

The forRoot() method takes care of the global injector configuration for the Router.\nThe forChild() method has no injector configuration. It uses directives such as RouterOutlet and RouterLink.\nFor more information, see the forRoot() pattern section of the Singleton Services guide.

\n\n

Preloadinglink

\n

Preloading improves UX by loading parts of your app in the background.\nYou can preload modules or component data.

\n

Preloading moduleslink

\n

Preloading modules improves UX by loading parts of your app in the background so users don't have to wait for the elements to download when they activate a route.

\n

To enable preloading of all lazy loaded modules, import the PreloadAllModules token from the Angular router.

\n\n\nimport { PreloadAllModules } from '@angular/router';\n\n\n

Still in the AppRoutingModule, specify your preloading strategy in forRoot().

\n\n\nRouterModule.forRoot(\n appRoutes,\n {\n preloadingStrategy: PreloadAllModules\n }\n)\n\n\n

Preloading component datalink

\n

To preload component data, you can use a resolver.\nResolvers improve UX by blocking the page load until all necessary data is available to fully display the page.

\n

Resolverslink

\n

Create a resolver service.\nWith the CLI, the command to generate a service is as follows:

\n\n ng generate service \n\n

In your service, import the following router members, implement Resolve, and inject the Router service:

\n\n\nimport { Resolve } from '@angular/router';\n\n...\n\nexport class CrisisDetailResolverService implements Resolve<> {\n resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> {\n // your logic goes here\n }\n}\n\n\n

Import this resolver into your module's routing module.

\n\n\nimport { YourResolverService } from './your-resolver.service';\n\n\n

Add a resolve object to the component's route configuration.

\n\n{\n path: '/your-path',\n component: YourComponent,\n resolve: {\n crisis: YourResolverService\n }\n}\n\n

In the component, use an Observable to get the data from the ActivatedRoute.

\n\nngOnInit() {\n this.route.data\n .subscribe((your-parameters) => {\n // your data-specific code goes here\n });\n}\n\n

For more information with a working example, see the routing tutorial section on preloading.

\n

Troubleshooting lazy-loading moduleslink

\n

A common error when lazy-loading modules is importing common modules in multiple places within an application. You can test for this condition by first generating the module using the Angular CLI and including the --route route-name parameter, where route-name is the name of your module. Next, generate the module without the --route parameter. If the Angular CLI generates an error when you use the --route parameter, but runs correctly without it, you may have imported the same module in multiple places.

\n

Remember, many common Angular modules should be imported at the base of your application.

\n

For more information on Angular Modules, see NgModules.

\n

More on NgModules and routinglink

\n

You may also be interested in the following:

\n\n\n \n
\n\n\n" }