Introducing a variety of services into the system

From my understanding, services must be provided and injected, meaning each service needs to be placed inside the constructor like this:

constructor (private a: AService, private B: BService) {}

In my scenario, I have multiple services that all follow the same interface that I want to store in an array. How can I achieve this without repeating myself too much (since I already have to declare them twice for providing and injecting - correct?)? Using regular classes instead of injectable services wouldn't work for me because each service might need to utilize other services like HTTP, which also need to be injected.

Answer №1

Utilizing a simple helper array known as "registry" and a dedicated array provider, we can efficiently access all registered instances. See the example code snippet below:

export class Examples extends Array<ExampleService> {}

const examplesRegistry = [ExampleServiceA, ExampleServiceB];

export function buildExampleProviders() {
  return [
    ...examplesRegistry,
    {
      provide: Examples,
      useFactory: (...args) => new Examples(...args),
      deps: examplesRegistry,
    },
  ];
}

As illustrated, we can inject individual instances (like

protected exampleA: ExampleServiceA
) or all of them with protected examples: Examples)


Complete Example and Usage

/// example.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export abstract class ExampleService {
  someProperty = Math.random();
  abstract name: string;
}

@Injectable()
export class ExampleServiceA extends ExampleService {
  override name = 'a';
}

@Injectable()
export class ExampleServiceB extends ExampleService {
  override name = 'b';
}
/// examples.ts

export class Examples extends Array<ExampleService> {}

const examplesRegistry = [ExampleServiceA, ExampleServiceB];

export function buildExampleProviders() {
  return [
    ...examplesRegistry,
    {
      provide: Examples,
      useFactory: (...args) => new Examples(...args),
      deps: examplesRegistry,
    },
  ];
}
/// app.component.ts

import { Component } from '@angular/core';
import { ExampleServiceA } from './example.service';
import { buildExampleProviders, Examples } from './examples';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [buildExampleProviders()],
})
export class AppComponent {
  constructor(
    protected examples: Examples,
    protected exampleA: ExampleServiceA
  ) {}
}
<!-- app.component.html -->
<pre>
  <ng-container *ngFor="let ex of examples; let i = index">
  examples[{{i}}]:
    .name: {{ ex.name }}
    .someProperty: {{ ex.someProperty }}
    .constructor.name: {{ ex.constructor.name }}
  </ng-container>

  exampleA:
    .name: {{ exampleA.name }}
    .someProperty: {{ exampleA.someProperty }}
    .constructor.name: {{ exampleA.constructor.name }}
</pre>

To explore this further, check out the example code on StackBlitz: https://stackblitz.com/edit/angular-ivy-3kaagr?file=src/app/examples.ts

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

Combine two redux actions and access the modified state in the second action

I'm facing a dilemma with a straightforward problem that I believe I have a solution for, but I'm uncertain about the effectiveness of my idea. Essentially, in the current state of things, I have an array property that requires updating just bef ...

MongoDB Node.js throws a RangeError when trying to push a message that exceeds the maximum call stack size

I'm currently delving into the world of building a MEAN app and encountering an issue when attempting to append a message to a messages array within my User Model. While I can successfully create a new message and pass the user object, I face an error ...

Missing folders in npm package

After successfully creating and publishing a private npm package, I noticed an inconsistency in the file structure when installing it on another project: Library.Util | |__index.js | |__package.json The original file structure of the package includes a t ...

Unable to run TypeScript on Ubuntu due to compilation errors

throw new TSError(formatDiagnostics(diagnosticList, cwd, ts, lineOffset)) ^ TSError: ⨯ Unable to compile TypeScript Cannot find type definition file for 'jasmine'. (2688) Cannot find type definition file for 'node'. (2688) api/ ...

The orderBy function is not functioning properly when used in conjunction with ng

I attempted to replicate the functionality demonstrated here, which involves organizing an array of objects in the view using the filter orderBy. While my code is functioning properly, when I apply the orderBy filter, nothing appears in the view. You can ...

How does the call method on array.prototype.includes work with arguments x and y?

Curious about the functionality of array.prototype.includes.call(x, y);. Discovered that includes() confirms if an array has the specified value and provides a true or false result. Learned that call() invokes this alongside any optional arguments. The ...

Using Angular2, assign a value to the session and retrieve a value from the session

I am having trouble getting and setting a session. Here is my code: login_btnClick() { var NTLoginID = ((document.getElementById("NTLoginID") as HTMLInputElement).value); this._homeService.get(Global.BASE_USER_ENDPOINT + '/EmployeeDe ...

NextJs does not allow external synchronous scripts

I am currently working with Next.js version 11.x Whenever I attempt to add an external script like the example below, I encounter an error when executing the yarn build. <Head> <link rel="stylesheet" type=" ...

Transform specific data binding values into JSON format using Knockout.js

Just dipping my toes into the world of knockoutjs, I've got this viewmodel set up: var Testing = function(){ this.Username = ko.observable(""); this.Password = ko.observable(""); this.email = ko.observable(""); } I'm tasked with ...

Select dropdown in AngularJS for Date formatting

After retrieving json data from a web API, I found that it contains a series of datetimes. My goal is to select a specific datetime from a dropdown list. While I can populate the list without any issues, the formatting appears to be incorrect and I'm ...

When the boolean is initially set to false, it will return true in an if statement without using

My Angular component contains a component-level boolean variable and an onClick event. Here's what the HTML file looks like: <div class="divClass" (click)="onClick($event)"></div> The relevant code from the TypeScript file is as follows: ...

Tips on converting comma-separated values into HTML table rows using <tr> tags

JSON Data { "catalog_name": ["Sistem Autodownline ", "Karipap Pusing Ayu"], "price": ["100", "8"], "qty": "", "qty2": ["", ""], "total_qty": "", "total": "", "mem": "10", "email_2": "", "ic_add": "890527-08-6136", "c ...

Encountering an issue with the 'createObjectURL' function in URL, resulting in overload resolution failure when using npm file-saver

While working on my angular app, I encountered a situation where I needed to download user details uploaded as a Word document to my local machine using the angular app. Successfully, I was able to upload and save this data to my database, getting its byte ...

Using a jQuery prompt to ensure data is saved when leaving the page

Is there a way to prompt users to save data on specific pages before they leave them? I'm thinking of using the jQuery UI dialog box. I understand that I can utilize the onbeforeunload event to recognize when a user is about to leave the current page, ...

The getelementbyid function is unable to locate the specified button identifier

As I dive into the world of Javascript, I wanted to create a simple input form with a corresponding response field on my website. However, I encountered an issue where using a basic submit button caused the page to refresh and erase the textfields before ...

Show the React component upon clicking the Add button

I recently developed a React component that reveals a new section whenever the user clicks the "New Section" button. However, I have encountered an issue - I would like the component to display multiple sections based on how many times the button is clic ...

What strategies can be used to manage Error return types in Typescript?

I have a situation where I need to handle either an object of type Person or an Error being returned by a function. My goal is to read the values of keys in the returned object only if it's not an Error. The code snippet below throws an error on the ...

Is it possible to utilize a variable from a Higher Order Function within a different generation function?

I am facing a dilemma with the need to utilize email = user.email in newcomment['comments/'+id] = {id,comment,email,date}. However, I am unable to incorporate email = yield user.email or yield auth.onAuthStateChanged(user => {email = user.em ...

Monochrome Effect Triggered by Cursor Hover

I'm attempting to use Javascript with HTML5 canvas to convert an image to greyscale, but I seem to be missing something in my code. Can anyone spot the error? I feel like I'm very close! function grayscaleConversion(str) { // Access the Canv ...

Cannot retrieve the array stored within my object

Trying to navigate my way through Angular as a newcomer (just transitioning from AngularJS) I'm working with an api call that returns an object containing a subdocument (array). The structure of the object returned by the api is: contact:{ first_ ...