Resolve cyclic dependency caused by utilizing the useFactory parameter

I am working with an injectable service that utilizes the useFactory attribute to determine whether it should be injected or if an implemented type should be used instead.

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { signatureConfigurationServiceFactory } from '../../environments/services.factories';
import { RestService } from '.';

@Injectable({
  providedIn: 'root',
  useFactory: signatureConfigurationServiceFactory,
  deps: [Router, RestService]
})
export class SignatureConfigurationService {

  constructor(public router: Router, public restService: RestService) {
  }

  // ... 
}

The factory function is located in a separate file named services.factories.ts. I have set it up this way so that I can easily swap it out using fileReplacements during the ng build process with another similar file containing a different factory function.

import { Router } from '@angular/router';
import { RestService } from '../app/services';
import { SignatureConfigurationService } from '../app/services/signature-configuration.service';

export let signatureConfigurationServiceFactory = (router: Router, restService: RestService) => {
  return new SignatureConfigurationService(router, restService);
};

However, I am encountering a circular reference issue since my service references the factory and vice versa.

While the Angular documentation suggests using forwardRef to resolve such issues, the examples provided did not seem applicable to my scenario.

What is the best approach to break out of this circular dependency while still keeping the factory method in a separate file?

Answer №1

I managed to find a solution.

As part of my approach, I introduced a new class named ServiceFactoriesRegistry. This class houses all the factory functions as static properties of a type that is a function returning any, eliminating the need to import the service file directly.

import { Router } from '@angular/router';
import { RestService } from '.';

export class ServiceFactoriesRegistry {
  public static signatureConfigurationServiceFactory: (router: Router, restService: RestService) => any;
}

To implement this, I modified the existing file containing the factory function to assign the factory method to this newly created registry. The file, named services.factories.ts, which will be swapped out at build time, depends on both the factory method and the service itself.

import { Router } from '@angular/router';
import { RestService } from '../app/services';
import { ServiceFactoriesRegistry } from '../app/services/service-registry';
import { SignatureConfigurationService } from '../app/services/signature-configuration.service';

export function registerServices() {
  ServiceFactoriesRegistry.signatureConfigurationServiceFactory = (router: Router, restService: RestService) => {
    return new SignatureConfigurationService(router, restService);
  };
}

This registration function is called within my AppModule:

export class AppModule {
  constructor() {
    registerServices();
  }
}

Within the service, I make use of the factory registry. While there is a dependency on the registry itself, there isn't a direct dependency on the service factory method since it's registered externally.

@Injectable({
  providedIn: 'root',
  useFactory: ServiceFactoriesRegistry.signatureConfigurationServiceFactory,
  deps: [Router, RestService]
})
export class SignatureConfigurationService

Answer №2

When injecting variables in the constructor, there is no need to declare them separately. Declaring them in both places is redundant.

Instead of:

export class SignatureConfigurationService {
router: Router;
restService: RestService;

constructor(router: Router, restService: RestService) {
this.router = router;
this.restService = restService;
}

You can simplify it to:

export class SignatureConfigurationService {

  constructor(private router: Router,private restService: RestService) {}

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

The value of Angular Input remains unchanged within a FormArray

I am experiencing an issue with the Sequence No in my PreprocessingForm's FormArray. When I add a new row, the Sequence No does not change as expected. <tr class="mat-row" *ngFor="let dynamic of PreprocessingForm.controls.arithmeticI ...

Retrieve data from a Firestore document in an Ionic application

I have a service that retrieves a specific document from Firestore using the getBidremains method. The method in the TypeScript class is called in ngOnInit like this: this.userInfo = this.firestoreService.getBidremains(userId).valueChanges().subscribe(da ...

The attribute 'map' is not found on the data type 'Observable<[{}, {}]>'

Struggling to incorporate map, PublishReplay, and other rxjs functions into Angular6, I keep encountering a compilation error stating "Property 'map' does not exist on type 'Observable<[{}, {}]>'" every time. The same issue arises ...

Divs are not being organized into rows correctly due to issues with Bootstrap styling

I have implemented Bootstrap in my Angular application. The stylesheet link is included in my Index.html file as follows: <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css"> In addition to that, I have listed Bootstrap a ...

What is the reason behind the never return value in this typescript template?

As demonstrated in this example using TypeScript: Access TypeScript Playground type FirstOrSecond<condition, T1, T2> = condition extends never ? T1 : T2 type foo = never extends never ? () => 'hi' : (arg1: never) => 'hi' ...

Angular - Exploring the process of creating a secondary standalone build for a specific section of an application

I have created an Angular 4 (or 5) application with a specific structure as shown in the image below: https://i.sstatic.net/zK1BM.png Now, I need to develop a separate standalone angular application where only a selected group of Angular components from ...

Having issues with my Angular 4 + MVC web application not functioning properly in Internet Explorer

After creating an application using Angular 4 and MVC, I noticed a problem specifically with Internet Explorer. The application runs smoothly on Chrome, Firefox, and other browsers except for IE. Does anyone have any suggestions on how to resolve this br ...

Is there a way to obtain the coordinates of an SVG element by simply clicking on a specific point?

I'm still learning about SVG and I'm trying to trigger an event that captures the click coordinates when clicking on the SVG. I have a few questions: Since I'm using Angular, I'm unsure if it's possible to keep my function in th ...

Connecting the mat-progress bar to a specific project ID in a mat-table

In my Job Execution screen, there is a list of Jobs along with their status displayed. I am looking to implement an Indeterminate mat-progress bar that will be visible when a Job is executing, and it should disappear once the job status changes to stop or ...

Retrieve the template parameter from a generic type

While I have experience extracting string from string[], this particular scenario is proving to be quite challenging: type example<T = boolean> = true; // with just "example", how can I retrieve the template parameter "boolean" in this case? type T ...

The function is trying to access a property called addclass on an object

When I pass a dom node along with a classname to a directive, the directive should add a class to the passed dom element. However, I am encountering an error that says "Cannot read property addclass of undefined." For reference, please visit this plnkr l ...

Adjust validation message and minimum value when radio button is altered in Angular application

Seeking a way to dynamically set a validation message and minimum value based on a radio button selection. Currently, there are two radio buttons for either 9 or 18 holes, each with a corresponding input (used in a golf handicap calculator app). The goal i ...

Request for /Account after Keycloak token request in Angular app

I have been working on an Angular and Keycloak project. I followed a tutorial that helped me integrate Keycloak into Angular, which can be found here: https://www.npmjs.com/package/keycloak-angular My public client is able to request a token, but when it ...

Guide to incorporating Angular 2 code into your Rails application

As a beginner in Ruby on Rails, I have recently learned some CRUD functionalities with RoR. Additionally, I am just starting my journey with Angular 2 and currently learning the basics. I noticed that RoR has its own HTML template engine similar to Angula ...

There seems to be an issue with calling this particular expression. The elements within the type 'string | ((searchTerm: string) => Promise<void>) | []' are not all callable

Here is my unique useResults custom hook: import { useEffect, useState } from 'react'; import yelp from '../api/yelp'; export default () => { const [results, setResults] = useState([]); const [errorMessage, setErrorMessage] = us ...

Add an asterisk before each line of comment when working in a TypeScript file using the VS Code IDE

Within my VS Code workspace, I am using the Typescript language and would like to format my comments across multiple lines with a specific style (look out for the star character) /** *@desc any text * any text */ However, when I attempt to write a comm ...

A guide to extracting attribute values from HTML using Angular 4

I am currently working on an Angular 4 project where I needed to implement drag and drop functionality. To achieve this, I utilized the ng2-dragula plugin. Now, I have a new requirement which involves extracting the data-id attribute from each row after it ...

Access my web application to easily download Jira tickets in XML format with just a single click

In order to retrieve all tickets from Jira within a specific time period, I currently have to manually extract them by clicking on the extract button in XML format. Subsequently, I download this XML file onto my web application and save it into the databa ...

Using Lodash to Substitute a Value in an Array of Objects

Looking to update the values in an array of objects, specifically the created_at field with months like 'jan', 'Feb', etc.? One way is to loop through using map as demonstrated below. However, I'm curious if there's a more co ...

Angular Custom Pipe - Grouping by Substrings of Strings

In my Angular project, I developed a custom pipe that allows for grouping an array of objects based on a specific property: import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'groupBy'}) export class GroupByPipe impleme ...