Shared Modules for Reusable Components in Angular

In Angular applications, especially large-scale enterprise apps, maintaining reusable code is crucial for consistency, scalability, and maintainability. One of the best practices is to create Shared Modules, which can hold commonly used components, directives, and pipes. These modules can then be imported across multiple feature modules, reducing code duplication and improving modularity.

In this guide, we will cover:

  1. Introduction to Shared Modules
  2. Benefits of using a Shared Module
  3. Creating a Shared Module
  4. Importing a Shared Module into Feature Modules
  5. Practical examples: reusable buttons, forms, and utility components
  6. Best practices for Shared Modules
  7. Conclusion

1. Introduction to Shared Modules

A Shared Module is an Angular module specifically designed to bundle reusable functionality. Typically, it includes:

  • Common Components: Buttons, headers, footers, modals
  • Directives: Custom validators, styling directives
  • Pipes: Date formatting, currency formatting, utility transformations
  • Common Angular modules: CommonModule, FormsModule, ReactiveFormsModule

Shared Modules promote reusability, reduce repetition, and help keep the codebase clean and organized.


2. Benefits of Using a Shared Module

Using a Shared Module has several advantages:

  1. Centralized reusable code
    • Components, pipes, and directives are written once and used in multiple modules.
  2. Simplified imports
    • Instead of importing individual components or Angular modules, importing the Shared Module provides all necessary functionality.
  3. Consistency across the application
    • Shared UI elements and utility functions maintain a consistent look and behavior.
  4. Reduced code duplication
    • Helps maintain DRY (Don’t Repeat Yourself) principles.
  5. Easy maintenance
    • Updates in the shared module automatically reflect across all modules that import it.

3. Creating a Shared Module

Angular CLI makes it easy to generate a module. Use the following command:

ng generate module shared

This creates a folder shared with shared.module.ts. Next, we add reusable components, directives, and pipes.

3.1 Example: Shared Button Component

Generate a button component:

ng generate component shared/button

button.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-button',
  template: <button [ngClass]="style" (click)="handleClick()">{{ label }}</button>,
  styles: [`
.primary { background-color: blue; color: white; }
.secondary { background-color: gray; color: white; }
`] }) export class ButtonComponent { @Input() label: string = 'Click Me'; @Input() style: string = 'primary'; @Output() clicked = new EventEmitter<void>(); handleClick() {
this.clicked.emit();
} }

3.2 Adding Shared Pipes

Example: A simple CapitalizePipe:

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

@Pipe({ name: 'capitalize' })
export class CapitalizePipe implements PipeTransform {
  transform(value: string): string {
if (!value) return '';
return value.charAt(0).toUpperCase() + value.slice(1);
} }

3.3 Adding Shared Directives

Example: HighlightDirective:

import { Directive, ElementRef, Input, OnChanges } from '@angular/core';

@Directive({ selector: '[appHighlight]' })
export class HighlightDirective implements OnChanges {
  @Input() appHighlight: string = 'yellow';

  constructor(private el: ElementRef) {}

  ngOnChanges() {
this.el.nativeElement.style.backgroundColor = this.appHighlight;
} }

3.4 Register Components, Pipes, and Directives in SharedModule

shared.module.ts

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

import { ButtonComponent } from './button/button.component';
import { CapitalizePipe } from './pipes/capitalize.pipe';
import { HighlightDirective } from './directives/highlight.directive';

@NgModule({
  declarations: [
ButtonComponent,
CapitalizePipe,
HighlightDirective
], imports: [
CommonModule,
FormsModule,
ReactiveFormsModule
], exports: [
ButtonComponent,
CapitalizePipe,
HighlightDirective,
CommonModule,
FormsModule,
ReactiveFormsModule
] }) export class SharedModule {}

Explanation:

  • declarations include components, pipes, and directives.
  • imports include Angular modules needed inside SharedModule.
  • exports make these components, directives, pipes, and imported modules available to any module that imports SharedModule.

4. Importing Shared Module into Feature Modules

After creating a SharedModule, it can be imported into any feature module to provide reusable functionality.

4.1 Feature Module Example

product.module.ts

import { NgModule } from '@angular/core';
import { ProductListComponent } from './product-list/product-list.component';
import { ProductDetailComponent } from './product-detail/product-detail.component';
import { SharedModule } from '../shared/shared.module';

@NgModule({
  declarations: [
ProductListComponent,
ProductDetailComponent
], imports: [
SharedModule
] }) export class ProductModule {}

Explanation:

  • SharedModule provides reusable components, pipes, and directives.
  • ProductListComponent and ProductDetailComponent can now use app-button, capitalize, and appHighlight without importing them individually.

4.2 Usage in Feature Component Template

<app-button label="Add Product" style="primary" (clicked)="addProduct()"></app-button>
<p>{{ 'angular shared module' | capitalize }}</p>
<p appHighlight="lightgreen">Highlighted Text</p>

Explanation:

  • Button component, pipe, and directive are now reusable in the product module.

5. Practical Examples

5.1 Reusable Form Components

Shared module can include input fields, select boxes, and validation components.

// shared/input-field/input-field.component.ts
import { Component, Input } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-input-field',
  template: `
&lt;label&gt;{{ label }}&lt;/label&gt;
&lt;input &#91;formControl]="control" &#91;type]="type" /&gt;
&lt;small *ngIf="control.invalid &amp;&amp; control.touched"&gt;{{ errorMessage }}&lt;/small&gt;
` }) export class InputFieldComponent { @Input() label: string = ''; @Input() control: FormControl = new FormControl(); @Input() type: string = 'text'; @Input() errorMessage: string = 'Invalid input'; }

Feature Module Usage:

<form [formGroup]="userForm">
  <app-input-field
label="Username"
&#91;control]="userForm.get('username')"
errorMessage="Username is required"&gt;
</app-input-field> </form>

5.2 Reusable Utility Components

  • ModalComponent for alerts
  • SpinnerComponent for loading indicators
  • CardComponent for UI layout

All can be declared in SharedModule and imported wherever needed.


6. Best Practices for Shared Modules

  1. Include only reusable items
    • Do not include feature-specific components.
  2. Export Angular core modules
    • Commonly used modules like FormsModule and ReactiveFormsModule should be exported.
  3. Avoid providers in SharedModule
    • Providing services here may create multiple instances; use CoreModule for singleton services.
  4. Use consistent naming
    • Prefix shared components, directives, and pipes with Shared or app- to avoid conflicts.
  5. Keep SharedModule lightweight
    • Avoid large feature-specific components to maintain reusability.
  6. Organize folder structure
src/app/shared/
  button/
button.component.ts
input-field/
input-field.component.ts
pipes/
capitalize.pipe.ts
directives/
highlight.directive.ts
shared.module.ts

Comments

Leave a Reply

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