What is a Module in Angular and Why is it Important?
Question 1: What is an Angular Module and why is it important?
Answer: An Angular module (NgModule) is a container for organizing related components, directives, pipes, and services. It’s important because it:
- Helps organize code into cohesive blocks
- Controls feature and dependency boundaries
- Provides compilation context for components
- Contributes to efficient bundling
Example of a basic module:
@NgModule({
declarations: [
HomeComponent,
UserListComponent,
UserDetailComponent
],
imports: [
CommonModule,
RouterModule,
SharedModule
],
exports: [
HomeComponent
],
providers: [
UserService
]
})
export class FeatureModule { }
Question 2: What are the different types of Angular Modules?
Answer:
// 1. Root Module
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule, // Important: Only in root
AppRoutingModule,
CoreModule,
SharedModule
],
bootstrap: [AppComponent]
})
export class AppModule { }
// 2. Feature Module
@NgModule({
imports: [
CommonModule, // Note: CommonModule, not BrowserModule
UserRoutingModule,
SharedModule
],
declarations: [
UserListComponent,
UserDetailComponent
]
})
export class UserModule { }
// 3. Shared Module
@NgModule({
imports: [CommonModule],
declarations: [
HeaderComponent,
FooterComponent,
LoadingSpinner
],
exports: [
HeaderComponent,
FooterComponent,
LoadingSpinner
]
})
export class SharedModule { }
// 4. Core Module
@NgModule({
imports: [CommonModule],
providers: [
AuthService,
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error('CoreModule is already loaded.');
}
}
}
Question 3: How do you handle module dependencies and imports?
Answer:
// Feature module with dependencies
@NgModule({
imports: [
CommonModule,
// Feature specific modules
MatTableModule,
MatPaginatorModule,
// Shared functionality
SharedModule,
// Routing
ProductRoutingModule
],
declarations: [
ProductListComponent,
ProductDetailComponent
],
providers: [
ProductService,
{
provide: PRODUCT_CONFIG,
useValue: defaultConfig
}
]
})
export class ProductModule { }
// Lazy loaded module
const routes: Routes = [
{
path: 'products',
loadChildren: () =>
import('./product/product.module')
.then(m => m.ProductModule)
}
];
Question 4: How do you optimize module loading and performance?
Answer:
// Lazy Loading
const routes: Routes = [
{
path: 'admin',
loadChildren: () =>
import('./admin/admin.module')
.then(m => m.AdminModule),
canLoad: [AuthGuard]
}
];
// Preloading Strategy
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
]
})
class AppModule { }
// Custom Preloading
export class CustomPreloading implements PreloadAllModules {
preload(route: Route, load: () => Observable<any>): Observable<any> {
return route.data?.['preload'] ? load() : EMPTY;
}
}
Question 5: What are common module patterns and best practices?
Answer:
// 1. Feature Module Pattern
@NgModule({
imports: [
CommonModule,
SharedModule,
StoreModule.forFeature('orders', orderReducer),
EffectsModule.forFeature([OrderEffects])
],
declarations: [
OrderListComponent,
OrderDetailComponent
],
providers: [
OrderService,
{
provide: OrderConfig,
useFactory: createOrderConfig,
deps: [CONFIG_TOKEN]
}
]
})
export class OrderModule { }
// 2. Shared Module Pattern
@NgModule({
imports: [CommonModule],
declarations: [
LoadingSpinner,
ErrorMessage,
HighlightDirective
],
exports: [
CommonModule, // Re-export common modules
LoadingSpinner,
ErrorMessage,
HighlightDirective
]
})
export class SharedModule {
static forRoot(): ModuleWithProviders<SharedModule> {
return {
ngModule: SharedModule,
providers: [SharedService]
};
}
}
Common Interview Follow-up Questions:
Q: What’s the difference between BrowserModule and CommonModule? A: BrowserModule is for the root module and provides browser-specific services, while CommonModule provides common directives and pipes. Only import BrowserModule in the root module, use CommonModule in feature modules.
Q: How do you prevent a module from being imported multiple times? A: Use the constructor guard pattern in core modules:
export class CoreModule { constructor(@Optional() @SkipSelf() parentModule: CoreModule) { if (parentModule) { throw new Error('CoreModule already loaded'); } } }
Q: What are forRoot() and forChild() methods? A: They’re module patterns for configuring providers:
@NgModule({}) class MyModule { static forRoot(config: Config): ModuleWithProviders<MyModule> { return { ngModule: MyModule, providers: [ { provide: CONFIG_TOKEN, useValue: config } ] }; } }
Q: How do you share services between modules? A: Use providedIn: ‘root’ for singleton services or declare in CoreModule:
@Injectable({ providedIn: 'root' // Preferred way }) export class GlobalService { } // Or in CoreModule @NgModule({ providers: [GlobalService] }) export class CoreModule { }
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.