Consider this Angular service and component setup using multi-provider pattern. What will the component display?
import { Component, Inject, InjectionToken } from '@angular/core'; export interface Logger { log(message: string): string; } export const LOGGER_TOKEN = new InjectionToken<Logger>('LOGGER_TOKEN'); export class ConsoleLogger implements Logger { log(message: string): string { return `ConsoleLogger: ${message}`; } } export class FileLogger implements Logger { log(message: string): string { return `FileLogger: ${message}`; } } @Component({ selector: 'app-log-display', template: `<div>{{logs.join(' | ')}}</div>`, providers: [ { provide: LOGGER_TOKEN, useClass: ConsoleLogger, multi: true }, { provide: LOGGER_TOKEN, useClass: FileLogger, multi: true } ] }) export class LogDisplayComponent { logs: string[] = []; constructor(@Inject(LOGGER_TOKEN) private loggers: Logger[]) { this.logs = this.loggers.map(logger => logger.log('Test')); } }
Remember that multi: true allows multiple providers to be injected as an array.
Using multi: true for the same token registers multiple providers. Angular injects an array of all provided instances. So both ConsoleLogger and FileLogger are injected, and their log methods are called, producing both messages joined by ' | '.
Choose the correct syntax to register two classes as multi-providers for the same injection token.
import { InjectionToken } from '@angular/core'; export const MY_TOKEN = new InjectionToken<string>('MY_TOKEN'); export class ServiceA {} export class ServiceB {}
Multi-providers require multi: true on all provider entries.
To register multiple providers for the same token, each provider must have multi: true. Otherwise, Angular will override previous providers or throw errors.
Given this provider setup, the app crashes with 'NullInjectorError: No provider for LOGGER_TOKEN!'. What is the cause?
import { Component, Inject, InjectionToken } from '@angular/core'; export interface Logger { log(message: string): string; } export class ConsoleLogger implements Logger { log(message: string): string { return `ConsoleLogger: ${message}`; } } export const LOGGER_TOKEN = new InjectionToken<Logger>('LOGGER_TOKEN'); @Component({ selector: 'app-root', template: `<app-log-display></app-log-display>`, providers: [ { provide: LOGGER_TOKEN, useClass: ConsoleLogger, multi: true } ] }) export class AppComponent {} @Component({ selector: 'app-log-display', template: `<div>{{logs}}</div>` }) export class LogDisplayComponent { logs: string = ''; constructor(@Inject(LOGGER_TOKEN) private loggers: Logger[]) { this.logs = loggers.map(l => l.log('Hi')).join(', '); } }
Consider Angular's hierarchical injectors and where providers are declared.
The multi-provider for LOGGER_TOKEN is declared only in AppComponent's providers. If LogDisplayComponent is used inside AppComponent's template, it inherits that injector. But if LogDisplayComponent is used elsewhere or lazy loaded, it may not see that provider. The error means the injector hierarchy does not have LOGGER_TOKEN provider accessible to LogDisplayComponent.
Why would a developer choose to use the multi-provider pattern in Angular dependency injection?
Think about scenarios where you want many implementations under one token.
Multi-provider pattern allows registering multiple providers for the same token. Angular injects them as an array. This is useful for plugins, logging, or extensible features where many implementations coexist.
Analyze the following Angular component using multi-provider pattern. What is the final value of the logs array?
import { Component, Inject, InjectionToken } from '@angular/core'; export interface Logger { log(message: string): string; } export const LOGGER_TOKEN = new InjectionToken<Logger>('LOGGER_TOKEN'); export class LoggerA implements Logger { log(message: string): string { return `A:${message}`; } } export class LoggerB implements Logger { log(message: string): string { return `B:${message}`; } } @Component({ selector: 'app-root', template: `<div>{{logs.join(',')}}</div>`, providers: [ { provide: LOGGER_TOKEN, useClass: LoggerA, multi: true }, { provide: LOGGER_TOKEN, useClass: LoggerB, multi: true }, { provide: LOGGER_TOKEN, useClass: LoggerA, multi: true } ] }) export class AppComponent { logs: string[] = []; constructor(@Inject(LOGGER_TOKEN) private loggers: Logger[]) { this.logs = this.loggers.map(l => l.log('hello')); } }
Multi-providers accumulate all providers in order of declaration.
All three providers are registered with multi: true for LOGGER_TOKEN. Angular injects an array with all three instances in the order declared. The map calls log('hello') on each, producing ["A:hello", "B:hello", "A:hello"].