Dependency Injection Providers & Injection Tokens

5 questions found

What is a provider in Angular's dependency injection system?

Beginner
A provider tells Angular's dependency injection system how to create a specific value or service when something asks for it. Most commonly this is just the class itself, but a provider can also describe using a different class, a fixed value, or a factory function instead.
@Injectable({ providedIn: 'root' })
export class LoggerService {
  log(message: string) { console.log(message); }
}

// The providedIn root option registers this as a provider automatically at the app level
Real-world example A logging service is registered once as a provider at the root of the app, letting any component or service throughout the entire app request and receive the exact same shared instance automatically.

Common follow-ups: What is the difference between providing a service at the root level versus inside a specific component?;What happens if two different providers are registered for the same service at different levels?

Services & Dependency Injection;Standalone Components

What is an injection token in Angular, and when would you need one instead of just injecting a class?

Intermediate
An injection token is used when you want to inject something that is not a class, such as a plain configuration object or a primitive value, since Angular's dependency injection normally relies on class types as unique identifiers. You create a token using InjectionToken and provide a matching value for it.
import { InjectionToken } from '@angular/core';

export const API_URL = new InjectionToken<string>('API_URL');

// Providing a value for the token
providers: [{ provide: API_URL, useValue: 'https://api.example.com' }]

// Injecting it elsewhere
constructor(@Inject(API_URL) private apiUrl: string) {}
Real-world example A service needs to know the correct API address to use, and instead of hardcoding it, the app provides that value through an injection token, letting different environments easily supply a different address.

Common follow-ups: Why can't you simply inject a plain string type the way you inject a class?;How would you provide a different value for the same injection token in a test environment?

Environment Variables & Configuration;Build Environments & Deployment

How does the useFactory provider option let you create a service with more complex setup logic?

Advanced
The useFactory option lets you provide a function that Angular calls to create the actual value, rather than just instantiating a class directly, useful when creating a service requires some conditional logic or depends on other injected services first.
export function loggerFactory(config: AppConfig) {
  return config.verboseLogging ? new VerboseLogger() : new SimpleLogger();
}

providers: [
  { provide: LoggerService, useFactory: loggerFactory, deps: [AppConfig] }
]
Real-world example An app provides either a detailed, verbose logging service or a simple, minimal one depending on a configuration setting, using a factory provider to decide which specific implementation to actually create at startup.

Common follow-ups: What does the deps array in a factory provider actually specify?;When would useFactory be preferred over simply writing conditional logic inside the service's own constructor?

Services & Dependency Injection;Angular Signals

What is the difference between providing a service using useClass and useExisting?

Intermediate
useClass tells Angular to create a brand new instance of a different class when something asks for the original one, useful for swapping implementations, such as during testing. useExisting instead reuses an already existing instance of another service, making both names point to the exact same single instance rather than creating a separate one.
// useClass, creates a new, separate instance of a different class
providers: [{ provide: LoggerService, useClass: MockLoggerService }]

// useExisting, reuses the same existing instance under a different name
providers: [{ provide: OldLoggerService, useExisting: LoggerService }]
Real-world example A test suite replaces the real LoggerService with a MockLoggerService using useClass, letting tests verify logging behavior without actually writing anything to the real console output.

Common follow-ups: What would happen if useExisting were used instead of useClass in a testing scenario needing separate instances?;When is useExisting useful in real production code, not just testing?

Testing with Jasmine & Karma;Services & Dependency Injection

What does providedIn root mean when registering an Angular service?

Beginner
Setting providedIn to root tells Angular to make this service available application wide, using a single shared instance, without needing to manually list it in any component's or module's providers array. This is the most common and simplest way to register a service in modern Angular.
@Injectable({ providedIn: 'root' })
export class CartService {
  items: string[] = [];
}

// Now injectable anywhere in the app without any extra setup
constructor(private cartService: CartService) {}
Real-world example A shopping cart service registered with providedIn root is automatically available to every component across the entire app, sharing the exact same cart data everywhere it is injected.

Common follow-ups: What is the benefit of providedIn root over manually adding a service to a providers array?;How would you provide a service only for a specific feature area instead of the entire app?

Services & Dependency Injection;NgModules & Modular Architecture