DEV Community

Renuka Patil
Renuka Patil

Posted on

9. Module System in Angular

Angular applications are modular by design, thanks to NgModules. An NgModule is a core building block that allows you to organize an app into cohesive blocks of functionality. This blog will explore AppModule, Feature Modules, and Shared Modules, showcasing their roles and how to implement them effectively.


What is an NgModule?

An NgModule is a class marked with the @NgModule decorator, which provides metadata about the module. Every Angular app must have at least one module, called the AppModule. The metadata defines the components, directives, pipes, and other modules that this module includes.

Key properties of @NgModule:

  • declarations: List of components, directives, and pipes in this module.
  • imports: Other modules to import for this module.
  • exports: Components, directives, and pipes to make available to other modules.
  • providers: Services used by this module.
  • bootstrap: Root component(s) to bootstrap when this module is bootstrapped.

1. The AppModule

The AppModule is the root module that bootstraps your Angular application. It acts as the entry point, initializing the application and loading essential modules.

Example: AppModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { FeatureModule } from './feature/feature.module';

@NgModule({
  declarations: [
    AppComponent // Root component of the application
  ],
  imports: [
    BrowserModule, // Essential for browser-based apps
    FeatureModule  // Importing a feature module
  ],
  providers: [],
  bootstrap: [AppComponent] // Bootstrapping the root component
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode
  • declarations: Registers components like AppComponent.
  • imports: Imports necessary modules like BrowserModule and custom FeatureModule.
  • bootstrap: Bootstraps the root component to start the app.

2. Feature Modules

A Feature Module encapsulates related functionality to manage the app's concerns, like user management, products, or reports. This promotes code reusability and maintainability.

Steps to Create a Feature Module

  1. Generate a new module using Angular CLI:
   ng generate module feature
Enter fullscreen mode Exit fullscreen mode
  1. Add components, services, or directives as needed.

Example: Feature Module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FeatureComponent } from './feature.component';

@NgModule({
  declarations: [
    FeatureComponent // Component specific to this module
  ],
  imports: [
    CommonModule // Provides Angular directives like *ngIf, *ngFor
  ],
  exports: [
    FeatureComponent // Make it available to other modules
  ]
})
export class FeatureModule { }
Enter fullscreen mode Exit fullscreen mode

FeatureComponent

import { Component } from '@angular/core';

@Component({
  selector: 'app-feature',
  template: `<h2>Welcome to the Feature Module!</h2>`
})
export class FeatureComponent { }
Enter fullscreen mode Exit fullscreen mode

Here, the FeatureModule manages its own component and logic, keeping the AppModule clean.


3. Shared Modules

A Shared Module consolidates common functionality used across multiple modules, such as utility components, directives, and pipes.

Steps to Create a Shared Module

  1. Generate the shared module:
   ng generate module shared
Enter fullscreen mode Exit fullscreen mode
  1. Include common components, directives, and pipes.

Example: Shared Module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeaderComponent } from './header/header.component';

@NgModule({
  declarations: [
    HeaderComponent // A reusable header component
  ],
  imports: [
    CommonModule
  ],
  exports: [
    HeaderComponent, // Exported for reuse
    CommonModule      // Re-export CommonModule for directives
  ]
})
export class SharedModule { }
Enter fullscreen mode Exit fullscreen mode

HeaderComponent

import { Component } from '@angular/core';

@Component({
  selector: 'app-header',
  template: `<header><h1>My App</h1></header>`
})
export class HeaderComponent { }
Enter fullscreen mode Exit fullscreen mode

Usage of Shared Module

import { SharedModule } from './shared/shared.module';

@NgModule({
  imports: [
    SharedModule // Reuse HeaderComponent and directives like *ngIf
  ]
})
export class FeatureModule { }
Enter fullscreen mode Exit fullscreen mode

4. Lazy Loading with Feature Modules

Feature modules can be loaded lazily to improve performance. This means modules are loaded only when needed, reducing the app's initial load time.

Routing for Lazy Loading

In the AppRoutingModule:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: 'feature', loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule) }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
Enter fullscreen mode Exit fullscreen mode

The FeatureModule is loaded only when the user navigates to /feature.


Best Practices for Using NgModules

  1. Keep AppModule Slim: Offload functionality to feature and shared modules.
  2. Use Shared Modules for Reusable Components: Avoid duplicating code.
  3. Lazy Load Modules: Optimize performance by loading modules on demand.
  4. Feature Modules for Domain-Specific Logic: Organize your app for scalability.

Conclusion

Understanding and effectively using NgModules—AppModule, Feature Modules, and Shared Modules—enhances your Angular application's scalability, maintainability, and performance. By separating concerns and reusing functionality, you create a robust and clean architecture.

Incorporate these strategies in your Angular projects to achieve a modular and high-performing application.


Tree Shaking: How Angular Removes Unused Code

Tree shaking is a term for dead code elimination, a process that removes unused JavaScript code during the build process. Angular leverages tree shaking through tools like Webpack and the Ahead-of-Time (AOT) compiler to optimize applications for performance.


How Tree Shaking Works in Angular

Tree shaking analyzes the dependency graph of an application. Any code or module not referenced in the dependency tree is excluded from the final JavaScript bundle.

For tree shaking to work:

  1. ESM (ECMAScript Modules): Modules must use ES6 import/export syntax.
  2. Static Analysis: Code must be statically analyzable (no dynamic imports that the compiler cannot resolve).

Example: Before Tree Shaking

// math-utils.ts
export const add = (a: number, b: number) => a + b;
export const subtract = (a: number, b: number) => a - b;

// main.ts
import { add } from './math-utils';

console.log(add(2, 3));
Enter fullscreen mode Exit fullscreen mode

In this example, subtract is unused. Tree shaking ensures subtract is not included in the final bundle.


Example: After Tree Shaking

The build process removes unused subtract, and the bundle contains only:

const add = (a, b) => a + b;
console.log(add(2, 3));
Enter fullscreen mode Exit fullscreen mode

Angular-Specific Tree Shaking

Angular's build tools, combined with the AOT compiler, enable tree shaking by pre-compiling templates and removing:

  1. Unused Angular modules and components.
  2. Debug-specific code.
  3. Unused imports from libraries like RxJS.

Optimizing for Tree Shaking

  1. Avoid Side Effects: Ensure modules have no unintended side effects.
   // Avoid this in your modules
   console.log('Side effect code');
Enter fullscreen mode Exit fullscreen mode
  1. Import Specific Functions: Import only what you need.
   // Instead of importing the entire library
   import { map } from 'rxjs/operators';
Enter fullscreen mode Exit fullscreen mode
  1. Enable AOT Compilation: Use the --prod flag during builds.
   ng build --prod
Enter fullscreen mode Exit fullscreen mode

Tree shaking significantly reduces bundle size, improving load times and performance.


Imports and Exports in Angular Modules: Organizing Code Effectively

Angular uses imports and exports in NgModules to manage application structure. Understanding these properties ensures better organization and reusability of code.


Using Imports in Modules

The imports array allows a module to use features from other modules.

Example: Importing CommonModule

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FeatureComponent } from './feature.component';

@NgModule({
  declarations: [FeatureComponent],
  imports: [CommonModule]
})
export class FeatureModule { }
Enter fullscreen mode Exit fullscreen mode

Here:

  • FeatureModule imports CommonModule to use directives like *ngIf and *ngFor.

Using Exports in Modules

The exports array makes components, directives, or pipes available to other modules.

Example: Exporting a Component

import { NgModule } from '@angular/core';
import { HeaderComponent } from './header.component';

@NgModule({
  declarations: [HeaderComponent],
  exports: [HeaderComponent] // Makes HeaderComponent reusable
})
export class SharedModule { }
Enter fullscreen mode Exit fullscreen mode

Combining Imports and Exports

Modules often re-export imported modules to simplify dependencies.

Example: Shared Module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

@NgModule({
  exports: [
    CommonModule,
    FormsModule // Re-exporting for other modules
  ]
})
export class SharedModule { }
Enter fullscreen mode Exit fullscreen mode

In this example, any module importing SharedModule automatically gains access to CommonModule and FormsModule.


Best Practices

  1. Feature Modules: Import only the modules required for a feature.
  2. Shared Modules: Consolidate and export commonly used modules and components.
  3. Lazy Loading: Use imports in routing to load modules only when needed.

Conclusion

  • Tree Shaking ensures only the necessary code is included in the final build, optimizing performance.
  • Properly using imports and exports organizes Angular modules, making the app scalable and maintainable.

These concepts form the foundation of building efficient Angular applications!


You can see these blogs to cover all angular concepts:

Beginner's Roadmap: Your Guide to Starting with Angular

  1. Core Angular Concepts
  2. Services and Dependency Injection
  3. Routing and Navigation
  4. Forms in Angular
  5. RxJS and Observables
  6. State Management
  7. Performance Optimization

Happy coding!

Top comments (0)