Feature modules are NgModules for the purpose of organizing code.
As your app grows, you can organize code relevant for a specific feature. This helps apply clear boundaries for features. With feature modules, you can keep code related to a specific functionality or feature separate from other code. Delineating areas of your app helps with collaboration between developers and teams, separating directives, and managing the size of the root module.
Feature modules vs. root modules
A feature module is an organizational best practice, as opposed to a concept of the core Angular API. A feature module delivers a cohesive set of functionality focused on a specific application need such as a user workflow, routing, or forms. While you can do everything within the root module, feature modules help you partition the app into focused areas. A feature module collaborates with the root module and with other modules through the services it provides and the components, directives, and pipes that it shares.
How to make a feature module
Assuming you already have a CLI generated app, create a feature module using the CLI by entering the following command in the root project directory. Replace CustomerDashboard with the name of your module. You can omit the “Module” suffix from the name because the CLI appends it:
ng generate module CustomerDashboard
This causes the CLI to create a folder called customer-dashboard with a file inside called customer-dashboard.module.ts with the following contents:
import { NgModule } from ‘@angular/core’;
import { CommonModule } from ‘@angular/common’;
@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class CustomerDashboardModule { }
The structure of an NgModule is the same whether it is a root module or a feature module. In the CLI generated feature module, there are two JavaScript import statements at the top of the file: the first imports NgModule, which, like the root module, lets you use the @NgModule decorator; the second imports CommonModule, which contributes many common directives such as ngIf and ngFor. Feature modules import CommonModule instead of BrowserModule, which is only imported once in the root module. CommonModule only contains information for common directives such as ngIf and ngFor which are needed in most templates, whereas BrowserModule configures the Angular app for the browser which needs to be done only once.
The declarations array is available for you to add declarables, which are components, directives, and pipes that belong exclusively to this particular module. To add a component, enter the following command at the command line where customer-dashboard is the directory where the CLI generated the feature module and CustomerDashboard is the name of the component:
ng generate component customer-dashboard/CustomerDashboard
This generates a folder for the new component within the customer-dashboard folder and updates the feature module with the CustomerDashboardComponent info:
src/app/customer-dashboard/customer-dashboard.module.ts
// import the new component
import { CustomerDashboardComponent } from ‘./customer-dashboard/customer-dashboard.component’;
@NgModule({
imports: [
CommonModule
],
declarations: [
CustomerDashboardComponent
],
})
The CustomerDashboardComponent is now in the JavaScript import list at the top and added to the declarations array, which lets Angular know to associate this new component with this feature module.
Types of Feature Modules
There are five general categories of feature modules which tend to fall into the following groups:
- Domain feature modules.
- Routed feature modules.
- Routing modules.
- Service feature modules.
- Widget feature modules.
While the following guidelines describe the use of each type and their typical characteristics, in real world apps, you may see hybrids.
Feature Module | Guidelines |
Domain | Domain feature modules deliver a user experience dedicated to a particular application domain like editing a customer or placing an order.
They typically have a top component that acts as the feature root and private, supporting sub-components descend from it. Domain feature modules consist mostly of declarations. Only the top component is exported. Domain feature modules rarely have providers. When they do, the lifetime of the provided services should be the same as the lifetime of the module. Domain feature modules are typically imported exactly once by a larger feature module. They might be imported by the root AppModule of a small application that lacks routing. |
Routed | Routed feature modules are domain feature modules whose top components are the targets of router navigation routes.
All lazy-loaded modules are routed feature modules by definition. Routed feature modules don’t export anything because their components never appear in the template of an external component. A lazy-loaded routed feature module should not be imported by any module. Doing so would trigger an eager load, defeating the purpose of lazy loading. That means you won’t see them mentioned among the AppModule imports. An eager loaded routed feature module must be imported by another module so that the compiler learns about its components. Routed feature modules rarely have providers for reasons explained in Lazy Loading Feature Modules. When they do, the lifetime of the provided services should be the same as the lifetime of the module. Don’t provide application-wide singleton services in a routed feature module or in a module that the routed module imports.
|
Routing | A routing module provides routing configuration for another module and separates routing concerns from its companion module.
A routing module typically does the following: ü Defines routes. ü Adds router configuration to the module’s imports. ü Adds guard and resolver service providers to the module’s providers. ü The name of the routing module should parallel the name of its companion module, using the suffix “Routing”. For example, FooModule in foo.module.ts has a routing module named FooRoutingModule in foo-routing.module.ts. If the companion module is the root AppModule, the AppRoutingModule adds router configuration to its imports with RouterModule.forRoot(routes). All other routing modules are children that import RouterModule.forChild(routes). ü A routing module re-exports the RouterModule as a convenience so that components of the companion module have access to router directives such as RouterLink and RouterOutlet. ü A routing module does not have its own declarations. Components, directives, and pipes are the responsibility of the feature module, not the routing module.
A routing module should only be imported by its companion module. |
Service | Service modules provide utility services such as data access and messaging. Ideally, they consist entirely of providers and have no declarations. Angular’s HttpClientModule is a good example of a service module.
The root AppModule is the only module that should import service modules. |
Widget | A widget module makes components, directives, and pipes available to external modules. Many third-party UI component libraries are widget modules.
A widget module should consist entirely of declarations, most of them exported. A widget module should rarely have providers. Import widget modules in any module whose component templates need the widgets. |
The following table summarizes the key characteristics of each feature module group.
Feature Module | Declarations | Providers | Exports | Imported by |
Domain | Yes | Rare | Top component | Feature, AppModule |
Routed | Yes | Rare | No | None |
Routing | No | Yes (Guards) | RouterModule | Feature (for routing) |
Service | No | Yes | No | AppModule |
Widget | Yes | Rare | Yes | Feature |