Welcome to the next installment in our Angular series! Today we’re tackling Angular Modules (NgModules), the essential building blocks for structuring scalable applications. Let’s break down this crucial concept.
Why Modules Matter
Modules help you:
- Organize related components/directives/pipes
- Manage dependencies efficiently
- Enable lazy loading for better performance
- Establish clear application boundaries
The @NgModule Decorator
Every Angular app has at least one module (the root module). Here’s what makes up a module:
typescript
@NgModule({ declarations: [ // Components, directives, pipes that belong to this module ], imports: [ // Other modules this module needs ], providers: [ // Services available to this module ], exports: [ // What other modules can use from this module ], bootstrap: [ // Root component (AppComponent for root module) ] }) export class AppModule { }
Types of Modules
- Root Module (
AppModule
):- Bootstraps the application
- Loaded when app starts
- Conventionally inÂ
app.module.ts
- Feature Modules:
- Group related functionality
- Examples:Â
UserModule
,ÂAdminModule
,ÂCheckoutModule
- Shared Module:
- Contains reusable components/directives/pipes
- Imported by multiple feature modules
- Core Module:
- Contains singleton services
- Imported only by root module
- Routing Module:
- Dedicated to routing configuration
Creating a Feature Module
Let’s create a ProductsModule
:
bash
ng generate module products
typescript
// products.module.ts @NgModule({ declarations: [ ProductListComponent, ProductDetailComponent, ProductFilterPipe ], imports: [ CommonModule, ProductsRoutingModule ], exports: [ ProductListComponent // Only if needed by other modules ] }) export class ProductsModule { }
Lazy Loading Modules
Dramatically improves initial load time by loading features on-demand:
typescript
// app-routing.module.ts const routes: Routes = [ { path: 'products', loadChildren: () => import('./products/products.module') .then(m => m.ProductsModule) } ];
Key benefits:
- Reduces initial bundle size
- Faster startup time
- Automatic code splitting
Shared Module Pattern
Create reusable UI components in a shared module:
typescript
// shared.module.ts @NgModule({ declarations: [ ButtonComponent, CardComponent, HighlightDirective, ExcerptPipe ], exports: [ ButtonComponent, CardComponent, HighlightDirective, ExcerptPipe, CommonModule, FormsModule ] }) export class SharedModule { }
Core Module for Singleton Services
Prevent accidental re-providing of services:
typescript
// core.module.ts @NgModule({ providers: [ AuthService, LoggerService, ApiService ] }) export class CoreModule { constructor(@Optional() @SkipSelf() parentModule: CoreModule) { if (parentModule) { throw new Error( 'CoreModule is already loaded. Import it in the AppModule only'); } } }
Module Organization Best Practices
- Feature Module Structure:textCopyDownload/products ├── products.module.ts ├── products-routing.module.ts ├── product-list/ ├── product-detail/ └── shared/
- When to Create New Modules:
- Logical feature boundaries
- Reusable UI components
- Route-based lazy loading needs
- Third-party library integration
- Common Pitfalls:
- Creating too many small modules
- Circular module dependencies
- Forgetting to export shared components
- Re-providing singleton services
Hands-On Exercise
- Create aÂ
UserModule
 with:- UserListComponent
- UserDetailComponent
- UserService
- Set up lazy loading
- Create a SharedModule with:
- A reusable AlertComponent
- A PhoneFormat pipe
What’s Next?
In Part 8, we’ll explore Dependency Injection & Services – Angular’s powerful system for managing application-wide functionality!