Offer identical service instances using various tokens

I am faced with a situation where I have 2 distinct interfaces:

export interface IUserService {
    ...
}

export interface IUserStorageService {
    ...
}

In order to consolidate functionalities from both interfaces, I have created a single service that implements them:

@Injectable()
export class UserService implements IUserService, IUserStorageService {
    ...
}

(Although it may not directly impact my question, I can easily convert these interfaces into abstract classes for smoother integration as tokens without the need for additional injection tokens.)
In Angular, interfaces cannot be used as tokens for providers, so I had to introduce injection tokens:

export let USER_SERVICE: InjectionToken<IUserService> = new InjectionToken<IUserService>("user.service");

export let USER_STORAGE_SERVICE: InjectionToken<IUserStorageService> = new InjectionToken<IUserStorageService>("user-storage.service");

With these injection tokens in place, I proceeded to globally map them to the single service class within my app.module.ts:

@NgModule({
    ...
    providers: [
        { provide: USER_SERVICE, useClass: UserService },
        { provide: USER_STORAGE_SERVICE, useClass: UserService }
    ],
    ...
})
export class AppModule {
    ...
}

Subsequently, I am now able to inject the service under different interfaces into my components:

// Component A - accesses service's functions through IUserService
constructor(@Inject(USER_SERVICE) private readonly userService: IUserService) {
}

// Component B - accesses service's functions through IUserStorageService
constructor(@Inject(USER_STORAGE_SERVICE) private readonly userStorageService: IUserStorageService) {
}

The challenge arises when Angular creates separate instances of UserService for each token whereas I require only one shared instance throughout the app.

Is there a way to achieve this desired behavior?

Answer №1

My previous project had a similar need: I needed to create a service for multiple components that had a dispatchEvent function and a subscribeToEvents function. To ensure that only the managing component can use dispatchEvent(), I decided to have the service implement two abstract classes. These abstract classes serve as tokens and provide clarity compared to using an injection token format. One of the abstract classes includes the dispatchEvents function while the other includes subscribeToEvents.

providers: [
    {provide: AbstractSettingsManager, useClass: SettingsEventService},
    {provide: AbstractSettingsConsumer, useExisting: AbstractSettingsManager}
  ],

By utilizing the useExisting key, it indicates to "use the existing token."

All components will share the same instance of ServiceEventService. However, by providing the AbstractSettingsManager/AbstractSettingsConsumer, TypeScript checks restrict components' access to either one of the service's functions.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue with Type-Error when accessing theme using StyledComponents and TypeScript in Material-UI(React)

I attempted to access the theme within one of my styled components like this: const ToolbarPlaceholder = styled('div')((theme: any) => ({ minHeight: theme.mixins.toolbar.minHeight, })); This information was found in the documentation: htt ...

Dealing with Unexpected Timeout Errors and Memory Leaks in Express/Typescript Using Jest, Supertest, and TypeORM

Currently, I am in the process of writing unit tests for an express API. Each test suite initiates a fresh instance of an express server before running the tests. Individually, each test suite runs smoothly without any problems. However, when executed tog ...

The functionality of this.router.navigate in Angular 15 seems to be off, as it is throwing an error saying it is not

Just have a quick question: Check out this code snippet... import { DOCUMENT } from '@angular/common'; import { Component, HostListener, Inject, NgZone } from '@angular/core'; import { Router } from '@angular/router'; @Compo ...

Programmatically populating the date picker in angular material

I have implemented the Angular Material date picker in one of the components of my Angular project. This specific component consists of two tabs, and I am using *ngIf to display only one tab at a time based on user interaction. When a user selects a date i ...

Next.js: Generating static sites only at runtime due to getStaticProps having no data during the build phase, skipping build time generation

I am looking to customize the application for individual customers, with a separate database for each customer (potentially on-premise). This means that I do not have access to any data during the build phase, such as in a CI/CD process, which I could use ...

The comparison between ng-content and router-outlet in Angular 2

Can someone help me decide on the best approach for structuring my components? i. The first approach involves using ng-content in the parent component and then creating child components enclosed within the parent's selector. For example, creating a ...

How can I verify that the value entered in an input field matches a specific date format such as "MM/dd/YYYY" using Angular?

I need to validate if a given value matches a specific date format such as "MM/dd/YYYY." Typescript file onValChange(event: Date) { const datePipe = new DatePipe('en-US'); const val = datePipe.transform(event, 'MM/dd/yyyy'); ...

Retrieve JSON Data in Angular 2

I've defined a model with the following structure: export class ExampleData{ id: string; url: string; } When I make a call to a service, it returns the JSON data shown below: [ { "id": "abc", "url": "/path/to/folder" }, { ...

Learn the process of importing data types from the Firebase Admin Node.js SDK

I am currently facing a challenge with importing the DecodedIDToken type from the https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken. I need this type to be able to assign it to the value in the .then() callback when v ...

Angular2: The definition of one or more providers for ... is missing: [?]

An error occurred: One or more providers for "AddressPage" were not defined: [?]. I have double-checked my code: @Injectable() export class NavService { .. } import {NavService} from '../../../providers/services/nav-service/nav-service'; @Com ...

Exploring the Power of Observables in Angular 2: Focusing on Targeting an Array Nested Within

I encountered a situation where I was successfully looping through objects in an array within my Angular 2 application using observables. In the client service file, my code looked like this: getByCategory(category: string) { const q = encodeURICompon ...

Changing the Express.Request.user type from optional User to required User for authorized routes: A guide

Currently, I am developing a server using Express and Typescript. I have integrated passport js for authenticating the routes I have set up. However, one issue that I encounter is that Express.Request.user is defined as Express.User | undefined. This means ...

Angular - No redirection occurs with a 303 response

Having an issue with redirection after receiving a 303 response from a backend API endpoint, which includes a Location URL to any subpage on my site. Upon attempting the redirect, an error is triggered: Error: SyntaxError: Unexpected token '<&ap ...

Entering _this

I am encountering an issue with my typescript file where it is failing TSLint. I need some help resolving this problem. The structure of the object in question is as follows: export default class Container extends Vue { // methods doSomething() { ...

What is the most effective strategy for handling JSON responses in Angular's Front End when subscribing to them using a forkJoin?

After researching various solutions for handling JSON mapping issues in the front end, I still haven't found a satisfactory answer. Despite trying different approaches, such as working with Root-object and nested interfaces, I'm struggling to map ...

TypeScript compiler encountering issue with locating immutable.js Map iterator within for of loop

I am currently facing a challenge with using immutable.js alongside TypeScript. The issue lies in convincing the TypeScript compiler that a Map has an iterator, even though the code runs smoothly in ES6. I am perplexed as to why it does not function correc ...

Angular4 + Universal + ng-bootstrap triggering an 'Unexpected token import' error

I recently made the leap to upgrade my angular version from 2+ to 4+ in order to take advantage of the universal package for server-side rendering, specifically for SEO purposes. Following the necessary steps and configurations outlined at https://github.c ...

Can we potentially extract shared components of a template?

As an example, I have numerous components designed for paged collections. Here is a template showcasing this: <div *ngIf="!isFormVisible"> <button class="btn" [ngClass]="{'btn-info': filtered, 'btn-default': !filtered}" (c ...

What is the contrast between element.getAttribute() value and a String in protractor?

When using protractor and typescript, I need to verify that the text saved in a textbox matches a certain string by comparing it with the resulting value of element.getAttribute("value"). Unfortunately, getText() does not work for this scenario b ...

What is the process for retrieving error logs in NPM ng2-file-upload within Angular 5?

Currently, I am utilizing the npm package ng2-file-upload to successfully upload an image file to S3. When using the onErrorItem hook to detect errors, I noticed that item.isError returns true. this.uploader.onErrorItem = ((item, response, status, headers ...