Hone logo
Hone
Problems

Implementing Multi-Provider Dependency Injection in Angular

Angular's dependency injection (DI) system is powerful, but sometimes you need more flexibility than a single provider allows. This challenge focuses on implementing a mechanism to dynamically select and use different providers for a service based on a configuration or environment setting. This is useful for scenarios like A/B testing, feature toggles, or adapting to different backend APIs.

Problem Description

You need to create an Angular service and a mechanism to inject different implementations of that service based on a configuration setting. The core service, DataService, has a single method, getData(), which should return different data depending on which provider is injected. You will be provided with two implementations: ImplementationA and ImplementationB. The application should read a configuration setting (simulated here as a simple string) to determine which implementation to use.

What needs to be achieved:

  1. Create an abstract DataService class with a getData() method that returns a Promise<string>.
  2. Implement two concrete classes, ImplementationA and ImplementationB, extending DataService and providing different data in their getData() methods.
  3. Create a configuration setting (a string variable) that determines which implementation to use ("A" or "B").
  4. Configure Angular's DI system to inject the correct DataService implementation based on the configuration setting.
  5. Create a component (AppComponent) that injects the DataService and calls getData() to display the retrieved data.

Key Requirements:

  • The code must be written in TypeScript.
  • The solution must be modular and well-structured.
  • The configuration setting should be easily modifiable without changing the component's code.
  • The getData() method should return a Promise to simulate asynchronous data fetching.

Expected Behavior:

  • If the configuration setting is "A", ImplementationA's getData() method should be called, and its data ("Data from A") should be displayed.
  • If the configuration setting is "B", ImplementationB's getData() method should be called, and its data ("Data from B") should be displayed.
  • If the configuration setting is anything other than "A" or "B", a default implementation (or an error handling mechanism) should be in place to prevent the application from crashing. For this challenge, a default implementation returning "Default Data" is acceptable.

Edge Cases to Consider:

  • What happens if the configuration setting is invalid (e.g., "C")?
  • How can the configuration setting be easily changed without modifying the component's code?
  • How can you ensure that the correct implementation is always injected?

Examples

Example 1:

Configuration Setting: "A"
Output: "Data from A"
Explanation: The application correctly injects and uses ImplementationA.

Example 2:

Configuration Setting: "B"
Output: "Data from B"
Explanation: The application correctly injects and uses ImplementationB.

Example 3:

Configuration Setting: "C"
Output: "Default Data"
Explanation: The application gracefully handles an invalid configuration setting by using the default implementation.

Constraints

  • The solution must be a complete, runnable Angular application.
  • The configuration setting must be defined outside of the AppComponent class.
  • The getData() method must return a Promise<string>.
  • The application should be relatively simple and focused on demonstrating the multi-provider concept. No complex UI or data manipulation is required.
  • The solution should be compatible with Angular version 14 or higher.

Notes

  • Consider using Angular's InjectionToken to provide a more type-safe way to configure the providers.
  • Think about how to make the configuration setting easily accessible and modifiable. A simple variable is sufficient for this challenge.
  • Focus on the core concept of dynamic provider selection. Error handling and advanced features are not the primary focus.
  • You can use a simple HttpClient mock if you want to simulate asynchronous data fetching, but it's not strictly required. A setTimeout within the getData() methods is sufficient.
Loading editor...
typescript