Angular Injection Token Creation Challenge
This challenge focuses on understanding and implementing Angular's dependency injection system by creating custom injection tokens. This is a fundamental technique for providing configuration values, services, or any other data to different parts of your Angular application without tightly coupling components or services.
Problem Description
Your task is to create a reusable injection token in Angular. This token will be used to provide a specific configuration value (e.g., an API base URL or a feature flag) to a service. You will then inject this value into the service and demonstrate its usage.
Key Requirements:
- Create an Injection Token: Define a constant using
InjectionTokento represent a unique identifier for your configuration value. - Provide the Value: Configure the Angular injector to provide a specific value for your custom token. This can be done at the module level or component level.
- Inject the Value: Create a service that injects the value associated with your custom token.
- Demonstrate Usage: Create a component that injects the service and displays the configuration value it received.
Expected Behavior:
When the application runs, the component should successfully display the configuration value that was provided via the injection token.
Edge Cases to Consider:
- What happens if the token is not provided? (Angular's default behavior is to throw an error, which is acceptable for this challenge).
- How would you provide different values for the same token in different parts of the application (e.g., for different environments)? (While not explicitly required to implement for this challenge, consider how your solution would accommodate this).
Examples
Example 1: Providing a simple string value
Scenario: Provide an API base URL.
Code Snippet (Conceptual - Not the full solution):
-
app.config.ts(or module provider):import { InjectionToken } from '@angular/core'; export const API_BASE_URL = new InjectionToken<string>('API_BASE_URL'); // ... in bootstrapApplication or NgModule providers providers: [ { provide: API_BASE_URL, useValue: 'https://api.example.com/v1' } ] -
my.service.ts:import { Inject, Injectable } from '@angular/core'; import { API_BASE_URL } from './app.config'; // or wherever defined @Injectable({ providedIn: 'root' }) export class MyService { constructor(@Inject(API_BASE_URL) private baseUrl: string) {} getApiEndpoint(): string { return `${this.baseUrl}/users`; } } -
my.component.ts:import { Component } from '@angular/core'; import { MyService } from './my.service'; @Component({ selector: 'app-my', template: `API Endpoint: {{ endpoint }}` }) export class MyComponent { endpoint: string; constructor(private myService: MyService) { this.endpoint = this.myService.getApiEndpoint(); } }
Output:
API Endpoint: https://api.example.com/v1/users
Explanation:
The API_BASE_URL token is defined and provided with a string value. The MyService injects this value and uses it to construct an API endpoint. The MyComponent injects MyService and displays the resulting endpoint.
Example 2: Providing an object configuration
Scenario: Provide a feature flag object.
Code Snippet (Conceptual):
-
feature.config.ts:import { InjectionToken } from '@angular/core'; export interface FeatureFlags { enableNewDashboard: boolean; showExperimentalFeature: boolean; } export const FEATURE_FLAGS = new InjectionToken<FeatureFlags>('FEATURE_FLAGS'); // ... in bootstrapApplication or NgModule providers providers: [ { provide: FEATURE_FLAGS, useValue: { enableNewDashboard: true, showExperimentalFeature: false } } ] -
feature.service.ts:import { Inject, Injectable } from '@angular/core'; import { FEATURE_FLAGS, FeatureFlags } from './feature.config'; @Injectable({ providedIn: 'root' }) export class FeatureService { constructor(@Inject(FEATURE_FLAGS) private flags: FeatureFlags) {} isNewDashboardEnabled(): boolean { return this.flags.enableNewDashboard; } }
Output (for a component injecting FeatureService and calling isNewDashboardEnabled()):
true
Explanation:
An object conforming to the FeatureFlags interface is provided via the FEATURE_FLAGS token. The FeatureService injects this object and exposes methods to check specific flag values.
Constraints
- The injection token must be created using Angular's
InjectionTokenclass. - The value provided to the token should be a primitive type (like
string,number,boolean) or a simple object/interface. - The service must use the
@Inject()decorator to explicitly inject the token's value. - The solution should be implemented in TypeScript.
- The primary focus is on demonstrating the creation and usage of the injection token, not complex application structure.
Notes
- Consider the generic type parameter for
InjectionToken(e.g.,new InjectionToken<string>('TOKEN_NAME')). This improves type safety. - You can provide values using
useValue,useFactory,useClass, oruseExisting. For this challenge,useValueis sufficient and recommended for simplicity. - While
providedIn: 'root'is common for services, you can also provide tokens at the module or component level using theprovidersarray. For this challenge, providing at the root or module level is acceptable. - Think about how naming your injection tokens clearly can improve the readability of your code.