Hone logo
Hone
Problems

Angular Injection Context: Custom Service Configuration

This challenge focuses on leveraging Angular's dependency injection system to provide context-specific configurations to services. You will learn how to create and use an injection context to tailor the behavior of a service based on where it's being provided in the application. This is a powerful technique for building reusable and configurable components and services.

Problem Description

Your task is to create an Angular application where a UserService can be configured with different data sources based on the module or component it's injected into. Specifically, you need to:

  1. Create a UserService: This service will have a method getUsers() that returns a list of users.
  2. Define an Injection Token: This token will represent the configuration for the UserService. The configuration should include a property like dataSource (e.g., 'api', 'mock').
  3. Provide Context-Specific Configurations: Implement two different configurations for the UserService.
    • Mock Configuration: When UserService is provided in a specific module (e.g., MockDataModule), it should use a mock data source.
    • API Configuration: When UserService is provided in another module (e.g., ApiDataModule), it should use an API data source.
  4. Demonstrate Usage: Create components in each respective module that inject and use the UserService, showcasing that they are indeed receiving the correct configuration.

Key Requirements:

  • The UserService itself should not know about the specific data source until runtime.
  • The configuration should be provided using Angular's dependency injection mechanisms.
  • Use InjectionToken for providing the configuration.
  • Ensure that when UserService is injected in different contexts, it behaves as expected.

Expected Behavior:

  • A component in MockDataModule should call userService.getUsers() and receive mock user data.
  • A component in ApiDataModule should call userService.getUsers() and receive user data that would conceptually come from an API (for this challenge, you can simulate this by returning a different set of static data).

Examples

Example 1: Mock Data Scenario

// mock-data.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserService } from './user.service';
import { USER_SERVICE_CONFIG, MockUserServiceConfig } from './user-service-config';
import { MockDataComponent } from './mock-data.component';

@NgModule({
  declarations: [MockDataComponent],
  imports: [CommonModule],
  providers: [
    UserService,
    {
      provide: USER_SERVICE_CONFIG,
      useValue: { dataSource: 'mock' } as MockUserServiceConfig
    }
  ],
  exports: [MockDataComponent]
})
export class MockDataModule { }

// mock-data.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-mock-data',
  template: `
    <h2>Mock Data Users:</h2>
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `
})
export class MockDataComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.users = this.userService.getUsers();
  }
}

Expected Output in MockDataComponent:

The component will display a list of users fetched from the mock data source. For instance:

<h2>Mock Data Users:</h2>
<ul>
  <li>Alice (Mock)</li>
  <li>Bob (Mock)</li>
</ul>

Explanation: The MockDataModule provides the UserService along with a configuration object specifying dataSource: 'mock'. When MockDataComponent injects UserService, it receives an instance configured to use mock data.

Example 2: API Data Scenario

// api-data.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UserService } from './user.service';
import { USER_SERVICE_CONFIG, ApiUserServiceConfig } from './user-service-config';
import { ApiDataComponent } from './api-data.component';

@NgModule({
  declarations: [ApiDataComponent],
  imports: [CommonModule],
  providers: [
    UserService,
    {
      provide: USER_SERVICE_CONFIG,
      useValue: { dataSource: 'api', apiUrl: '/api/users' } as ApiUserServiceConfig
    }
  ],
  exports: [ApiDataComponent]
})
export class ApiDataModule { }

// api-data.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
  selector: 'app-api-data',
  template: `
    <h2>API Data Users:</h2>
    <ul>
      <li *ngFor="let user of users">{{ user.name }}</li>
    </ul>
  `
})
export class ApiDataComponent implements OnInit {
  users: any[] = [];

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.users = this.userService.getUsers();
  }
}

Expected Output in ApiDataComponent:

The component will display a list of users fetched from the simulated API data source. For instance:

<h2>API Data Users:</h2>
<ul>
  <li>Charlie (API)</li>
  <li>David (API)</li>
</ul>

Explanation: The ApiDataModule provides the UserService with a configuration object specifying dataSource: 'api' and an apiUrl. When ApiDataComponent injects UserService, it receives an instance configured to fetch data from the API.

Constraints

  • The UserService should have a getUsers() method.
  • The configuration for the UserService should be provided via InjectionToken.
  • You must create at least two distinct modules that provide different configurations for the UserService.
  • The solution should be implemented in TypeScript.
  • No external libraries beyond Angular core are allowed for the core functionality.

Notes

  • Think about how to structure your configuration objects to hold the necessary data for each source.
  • Consider using interfaces to define the shape of your configuration objects for better type safety.
  • The UserService will need to access its injected configuration. How can you inject the configuration itself into the service?
  • For the purpose of this challenge, you can hardcode the "mock" and "API" data within the UserService or helper functions. The focus is on the injection context and how the service adapts.
Loading editor...
typescript