Angular’s modular architecture allows developers to build scalable and maintainable applications. One key aspect of this modularity is the use of forRoot
and forChild
methods in Angular modules. In this post, we’ll dive deep into these methods, exploring their purpose, differences, and how to use them effectively with a practical example.
Understanding the Basics
1. What is forRoot?
In Angular, forRoot
is a static method used to configure a module with essential providers and settings. It is typically called in the root module of the application during the import of a feature module. The primary purpose of forRoot
is to configure services that are intended to be singletons throughout the application.
2. What is forChild?
forChild
is used to configure feature modules that can be imported by other modules. It allows multiple instances of the module to be created, each with its own configuration. This is especially useful when the configuration can vary depending on the importing module.
Practical Examples
Configurable Logger Module
Let’s consider a practical example where we build a configurable logger module that logs messages to the console. We will use forRoot
and forChild
to configure the logger with different log levels in different parts of our application.
Logger Module
// logger.module.ts
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LoggerService, LogLevel } from './logger.service';
@NgModule({
imports: [CommonModule],
providers: [LoggerService],
})
export class LoggerModule {
static forRoot(logLevel: LogLevel): ModuleWithProviders<LoggerModule> {
return {
ngModule: LoggerModule,
providers: [
{ provide: LogLevel, useValue: logLevel },
],
};
}
static forChild(logLevel: LogLevel): ModuleWithProviders<LoggerModule> {
return {
ngModule: LoggerModule,
providers: [
{ provide: LogLevel, useValue: logLevel },
],
};
}
}
Logger Service
// logger.service.ts
import { Injectable, InjectionToken } from '@angular/core';
export const LogLevel = new InjectionToken<string>('logLevel');
@Injectable()
export class LoggerService {
constructor(private logLevel: string) {}
log(message: string): void {
if (this.logLevel === 'DEBUG') {
console.log(`[DEBUG] ${message}`);
}
}
}
App Module
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { LoggerModule } from './logger/logger.module';
import { LoggerService } from './logger/logger.service';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
LoggerModule.forRoot('DEBUG'), // Global configuration for the root module
],
providers: [LoggerService],
bootstrap: [AppComponent],
})
export class AppModule {}
Feature Module
// feature.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FeatureComponent } from './feature.component';
import { LoggerModule } from '../logger/logger.module';
import { LoggerService } from '../logger/logger.service';
@NgModule({
declarations: [FeatureComponent],
imports: [
CommonModule,
LoggerModule.forChild('INFO'), // Configuration for the feature module
],
providers: [LoggerService],
})
export class FeatureModule {}
Feature Component
// feature.component.ts
import { Component } from '@angular/core';
import { LoggerService } from '../logger/logger.service';
@Component({
selector: 'app-feature',
template: `
<div>
<h2>Feature Component</h2>
<button (click)="logMessage()">Log Message</button>
</div>
`,
})
export class FeatureComponent {
constructor(private loggerService: LoggerService) {}
logMessage(): void {
this.loggerService.log('This message will be logged because the log level is INFO in the feature module.');
}
}
App Component
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div>
<h1>Angular Logger Example</h1>
<app-feature></app-feature>
</div>
`,
})
export class AppComponent {}
In this example, the root module (AppModule
) configures the logger with a log level of ‘DEBUG’, while the feature module (FeatureModule
) configures the logger with a log level of ‘INFO’. The FeatureComponent
logs a message when a button is clicked, demonstrating how forRoot
and forChild
can be used to configure modules and their services in a hierarchical manner within an Angular application.