Importing and Exporting Modules in Angular

In Angular, modular architecture is key to building scalable, maintainable applications. Modules allow you to group related components, directives, pipes, and services together. Understanding how to import and export modules is essential for reusability and efficient code management. This post will explain how to import modules into other modules, export components, directives, and pipes for use across modules, and provide a practical example of sharing a custom pipe.

Understanding Angular Modules

Angular modules, also known as NgModules, are containers for a cohesive block of functionality. Each Angular application has at least one module, the AppModule. Additional modules are typically created for feature organization, shared utilities, and routing.

A module is defined using the @NgModule decorator, which takes metadata such as:

  • declarations: Components, directives, and pipes that belong to the module.
  • imports: Other modules that are needed by this module.
  • exports: Components, directives, and pipes that are exposed for use in other modules.
  • providers: Services available to the injector for dependency injection.
  • bootstrap: The main application view or root component.

Example of a basic module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MyComponent } from './my-component/my-component.component';

@NgModule({
  declarations: [MyComponent],
  imports: [CommonModule],
  exports: [MyComponent]
})
export class MyModule { }

In this example, MyComponent is declared in MyModule and also exported so other modules can use it.


Importing Modules into Other Modules

Angular allows you to import one module into another using the imports array in the @NgModule decorator. This is essential for reusing components, directives, and pipes defined in other modules.

The general syntax is:

@NgModule({
  imports: [
SomeModule,
AnotherModule
] }) export class FeatureModule { }

How Importing Works

When you import a module:

  • Angular makes all exported declarations (components, directives, pipes) of the imported module available in the current module.
  • Services provided in the imported module (if provided in the root or module-level injector) are accessible.
  • Modules imported into a feature module do not automatically become global; you must import them wherever needed.

Example: Importing a Shared Module

Suppose you have a SharedModule containing a custom pipe and a component:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomPipe } from './pipes/custom.pipe';
import { SharedComponent } from './components/shared.component';

@NgModule({
  declarations: [CustomPipe, SharedComponent],
  imports: [CommonModule],
  exports: [CustomPipe, SharedComponent]
})
export class SharedModule { }

To use the CustomPipe and SharedComponent in a feature module:

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

@NgModule({
  declarations: [FeatureComponent],
  imports: [CommonModule, SharedModule]
})
export class FeatureModule { }

Now, FeatureComponent can use CustomPipe in its template:

<p>{{ 'Angular' | custom }}</p>
<app-shared-component></app-shared-component>

Exporting Components, Directives, and Pipes

Exporting is what makes components, directives, and pipes available for use in other modules. Only declarations included in the exports array of an NgModule can be used by importing modules.

Rules for Exporting

  1. Only declarations (components, directives, pipes) can be exported.
  2. Services cannot be exported using the exports array; they are made available using providers.
  3. Modules themselves can be exported to make all their exported declarations available.

Syntax

@NgModule({
  declarations: [CustomPipe, CustomDirective, CustomComponent],
  exports: [CustomPipe, CustomDirective, CustomComponent]
})
export class SharedModule { }

This module can now be imported elsewhere to access all its exported declarations.


Practical Example: Sharing a Custom Pipe Across Modules

Creating a custom pipe and sharing it across modules is a common use case in Angular.

Step 1: Create the Pipe

Generate a pipe using Angular CLI:

ng generate pipe pipes/custom

Implement the pipe:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'custom'
})
export class CustomPipe implements PipeTransform {
  transform(value: string, ...args: any[]): string {
return value.toUpperCase();
} }

This simple pipe converts text to uppercase.


Step 2: Create a Shared Module

Create a SharedModule that declares and exports the pipe:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomPipe } from './pipes/custom.pipe';

@NgModule({
  declarations: [CustomPipe],
  imports: [CommonModule],
  exports: [CustomPipe]
})
export class SharedModule { }

Step 3: Import Shared Module into Other Modules

Now you can use CustomPipe in multiple feature modules by importing SharedModule:

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

@NgModule({
  declarations: [FeatureComponent],
  imports: [CommonModule, SharedModule]
})
export class FeatureModule { }

Step 4: Use the Pipe in Templates

In FeatureComponent template:

<p>{{ 'hello world' | custom }}</p>

The output will be:

HELLO WORLD

Sharing Components and Directives Across Modules

The same logic used for pipes applies to components and directives. Let’s create a reusable directive and component.

Custom Directive Example

import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef, private renderer: Renderer2) { }

  @HostListener('mouseenter') onMouseEnter() {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'yellow');
} @HostListener('mouseleave') onMouseLeave() {
this.renderer.setStyle(this.el.nativeElement, 'background-color', 'transparent');
} }

Reusable Component Example

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

@Component({
  selector: 'app-card',
  template: `
&lt;div class="card"&gt;
  &lt;ng-content&gt;&lt;/ng-content&gt;
&lt;/div&gt;
`, styles: [`
.card { padding: 20px; border: 1px solid #ccc; border-radius: 8px; }
`] }) export class CardComponent { }

Include in SharedModule

@NgModule({
  declarations: [CustomPipe, HighlightDirective, CardComponent],
  imports: [CommonModule],
  exports: [CustomPipe, HighlightDirective, CardComponent]
})
export class SharedModule { }

Use in Feature Module

@Component({
  selector: 'app-feature',
  template: `
&lt;app-card appHighlight&gt;
  &lt;p&gt;{{ 'angular modules' | custom }}&lt;/p&gt;
&lt;/app-card&gt;
` }) export class FeatureComponent { }

Best Practices for Module Organization

  1. Feature Modules: Group related functionality together. Each feature module should be self-contained.
  2. Shared Module: For reusable components, directives, and pipes used across multiple modules.
  3. Core Module: For singleton services and app-wide singletons. Import this module only in AppModule.
  4. Avoid Re-importing Shared Modules in Lazy-loaded Modules if they contain services, to prevent multiple instances.

Lazy Loading and Module Imports

Lazy-loaded modules only load when their route is activated. Importing shared modules into lazy-loaded modules works the same way. Ensure services intended to be singleton are provided in the CoreModule or with providedIn: 'root' to avoid multiple instances.


Common Pitfalls

  1. Forgotten Exports: Declaring a component or pipe in a module without exporting it will make it unavailable to other modules.
  2. Circular Dependencies: Avoid importing modules into each other in a circular manner.
  3. Duplicate Providers: Providing the same service in multiple modules can create multiple instances. Use providedIn: 'root' for singletons.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *