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:
- Introduction to Shared Modules
- Benefits of using a Shared Module
- Creating a Shared Module
- Importing a Shared Module into Feature Modules
- Practical examples: reusable buttons, forms, and utility components
- Best practices for Shared Modules
- 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:
- Centralized reusable code
- Components, pipes, and directives are written once and used in multiple modules.
- Simplified imports
- Instead of importing individual components or Angular modules, importing the Shared Module provides all necessary functionality.
- Consistency across the application
- Shared UI elements and utility functions maintain a consistent look and behavior.
- Reduced code duplication
- Helps maintain DRY (Don’t Repeat Yourself) principles.
- 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
andProductDetailComponent
can now useapp-button
,capitalize
, andappHighlight
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: `
<label>{{ label }}</label>
<input [formControl]="control" [type]="type" />
<small *ngIf="control.invalid && control.touched">{{ errorMessage }}</small>
`
})
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"
[control]="userForm.get('username')"
errorMessage="Username is required">
</app-input-field>
</form>
5.2 Reusable Utility Components
ModalComponent
for alertsSpinnerComponent
for loading indicatorsCardComponent
for UI layout
All can be declared in SharedModule and imported wherever needed.
6. Best Practices for Shared Modules
- Include only reusable items
- Do not include feature-specific components.
- Export Angular core modules
- Commonly used modules like
FormsModule
andReactiveFormsModule
should be exported.
- Commonly used modules like
- Avoid providers in SharedModule
- Providing services here may create multiple instances; use CoreModule for singleton services.
- Use consistent naming
- Prefix shared components, directives, and pipes with
Shared
orapp-
to avoid conflicts.
- Prefix shared components, directives, and pipes with
- Keep SharedModule lightweight
- Avoid large feature-specific components to maintain reusability.
- 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
Leave a Reply