Angular is a powerful framework for building large-scale, maintainable web applications. One of its core architectural concepts is the module system, which allows developers to organize the application into cohesive blocks of functionality. Modules help improve maintainability, scalability, and code reusability, making Angular applications easier to develop and manage.
In this guide, we will explore:
- What are Angular Modules?
- Types of Angular Modules
- Root Module
- Feature Modules
- Shared Modules
- Organizing large applications using modules
- Lazy loading and module optimization
- Best practices
- Practical examples with code
1. What Are Angular Modules?
Angular modules, defined using the @NgModule
decorator, are containers for components, directives, pipes, and services. They group related functionalities and allow Angular to compile and bootstrap the application efficiently.
Why Use Modules?
- Organize code logically
- Enable reusability of components, directives, and pipes
- Improve maintainability
- Allow lazy loading for performance optimization
- Simplify testing
Module Structure
A basic module includes:
- Declarations: Components, directives, and pipes belonging to the module
- Imports: Other modules whose exported classes are needed
- Providers: Services available within the module
- Bootstrap: Root component for bootstrapping the module (only for root module)
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
2. Types of Angular Modules
Angular modules can be categorized into three main types:
- Root Module
- Feature Modules
- Shared Modules
2.1 Root Module (AppModule)
The Root Module is the starting point of an Angular application. It bootstraps the application and typically contains global imports and providers.
Example
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [AppComponent, HeaderComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
Explanation:
declarations
include components used in this module.imports
include other modules needed globally.bootstrap
identifies the root component (AppComponent
) to launch the app.
2.2 Feature Modules
Feature Modules organize functionality into specific application domains, making code modular and maintainable. Each feature module may have its own components, services, and routes.
Creating a Feature Module
ng generate module products
Example: ProductsModule
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProductListComponent } from './product-list/product-list.component';
import { ProductDetailComponent } from './product-detail/product-detail.component';
import { RouterModule } from '@angular/router';
@NgModule({
declarations: [ProductListComponent, ProductDetailComponent],
imports: [
CommonModule,
RouterModule.forChild([
{ path: 'products', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailComponent }
])
]
})
export class ProductsModule {}
Benefits of Feature Modules:
- Modularize application features
- Reduce root module complexity
- Support lazy loading
- Improve maintainability and testability
2.3 Shared Modules
Shared Modules contain reusable components, directives, and pipes used across multiple modules.
Example: SharedModule
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HighlightDirective } from './highlight.directive';
import { CustomPipe } from './custom.pipe';
@NgModule({
declarations: [HighlightDirective, CustomPipe],
imports: [CommonModule],
exports: [HighlightDirective, CustomPipe, CommonModule]
})
export class SharedModule {}
Explanation:
declarations
include components, directives, and pipes.exports
allow other modules to use the declared components/pipes.imports
include commonly required modules likeCommonModule
.
Usage in Feature Module:
import { SharedModule } from '../shared/shared.module';
@NgModule({
imports: [SharedModule],
})
export class ProductsModule {}
3. Organizing Large Applications Using Modules
For enterprise applications, proper module organization is critical. A typical structure may include:
app/
app.module.ts
core/
core.module.ts
services/
shared/
shared.module.ts
components/
directives/
pipes/
features/
products/
products.module.ts
components/
users/
users.module.ts
components/
Core Module
The Core Module contains singleton services and app-wide providers. It is imported only in the root module.
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { AuthService } from './services/auth.service';
@NgModule({
providers: [AuthService]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error('CoreModule is already loaded. Import only in AppModule.');
}
}
}
Explanation:
SkipSelf
ensures CoreModule is loaded only once.- Provides singleton services for the entire app.
4. Lazy Loading Modules
Lazy loading improves application performance by loading feature modules only when needed.
Setting Up Lazy Loading
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{ path: 'products', loadChildren: () => import('./features/products/products.module').then(m => m.ProductsModule) }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Benefits:
- Reduces initial load time
- Efficient resource usage
- Improves user experience
5. Dependency Injection and Modules
Modules help organize providers for dependency injection:
- Root Module Providers: Available globally
- Feature Module Providers: Scoped to the feature module
- Shared Module Providers: Avoid providing services here to prevent multiple instances
6. Best Practices for Angular Modules
- Use Root Module for bootstrapping and global imports.
- Use Feature Modules for distinct application areas.
- Use Shared Module for reusable components, pipes, and directives.
- Use Core Module for singleton services.
- Avoid providing services in SharedModule to prevent multiple instances.
- Leverage lazy loading for feature modules.
- Keep modules cohesive – each module should serve a specific purpose.
- Follow naming conventions (
feature.module.ts
,shared.module.ts
,core.module.ts
). - Import
CommonModule
in every feature/shared module. - Keep module declarations and imports organized to improve readability.
7. Practical Examples
Example: E-Commerce Application
app/
app.module.ts
core/
core.module.ts
services/auth.service.ts
shared/
shared.module.ts
components/header.component.ts
pipes/currency.pipe.ts
features/
products/
products.module.ts
product-list.component.ts
product-detail.component.ts
users/
users.module.ts
user-profile.component.ts
AppModule:
@NgModule({
imports: [BrowserModule, AppRoutingModule, CoreModule, SharedModule],
bootstrap: [AppComponent]
})
export class AppModule {}
Lazy Loaded Products Module:
const routes: Routes = [
{ path: '', component: ProductListComponent },
{ path: ':id', component: ProductDetailComponent }
];
@NgModule({
imports: [CommonModule, RouterModule.forChild(routes), SharedModule],
declarations: [ProductListComponent, ProductDetailComponent]
})
export class ProductsModule {}
Leave a Reply