Angular 6 and above: The use of ProvidedIn in a submodule is leading to a circular dependency issue

A resolve service is being implemented using the new providedIn attribute.

This translations resolver is utilized in a protected module:

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

import { Observable , pipe } from 'rxjs';
import {map} from "rxjs/operators";

//This causes: "WARNING in Circular dependency detected:"
import {ProtectedModule} from "../../../protected/protected.module";

import { HttpHandlerService } from '../../http/http-handler.service';

@Injectable({
  providedIn: ProtectedModule //The import for this line is required
})
export class TranslationsResolverService {
  constructor(private _httpHandlerService : HttpHandlerService) { }
    resolve(): any {
      //Do Something...
    }
}

The translations resolver service is declared in the protected routing module:

import { NgModule }           from '@angular/core';
import {RouterModule, Routes} from '@angular/router';

import {AuthGuard} from "../core/resolvers/auth/auth.guard";
import {TranslationsResolverService} from "./../core/resolvers/translations/translations-resolver.service";

const routes: Routes = [
  {
    path : 'app' ,
    component: ProtectedComponent,
    resolve : {
      translations : TranslationsResolverService 
    },
    canActivate: [AuthGuard],
    ]
  }
];


@NgModule({
  imports : [RouterModule.forChild(routes)],
  exports : [RouterModule]
})
export class ProtectedRoutingModule { }

About importing the protected.module in the translations-resolver.service.ts to use it in the providedIn attribute, it triggers a WARNING about Circular dependency:

path/to/translations-resolver.service.ts -> 

protected/protected.module.ts ->

protected/protected-routing.module.ts -> 

path to translations-resolver.service.ts

The extra path (protected/protected.module.ts) is added due to the providedIn attribute.

To remedy this issue, the translationsResolver can be provided as an NgModule provider (in the providers array), but preference is towards it being an injectable provider.

Any suggestions on how to solve this?

Answer №1

Encountered the same issue recently. It seems that the solution is simply to avoid doing it altogether, as detailed in a discussion by one of the team members of Angular: https://github.com/angular/angular-cli/issues/10170#issuecomment-380673276

Apparently, providing services at the root module level makes them more efficiently tree shakeable.

The disappointment is mutual.

Answer №2

Latest Update - October 2019

After receiving 5 up-votes on this answer, I feel it's important to clarify that I have actually deviated from my initial advice mentioned below!

Given the widely accepted Angular policy of using providedIn: 'root', I made the decision to stick with this approach for the sake of clarity among other developers. So far, this choice has not caused any issues for me, but the concerns raised in my original post still hold true and should be kept in mind.

Original Post Overview

In my opinion, Angular has introduced some confusion with the syntax of providedIn, evident from the discussions seen in various GitHub threads:

The use of providedIn offers two primary advantages:

  1. It facilitates tree-shaking to eliminate unused services
  2. providedIn: 'root' guarantees a single instance of the service across the application

However, benefit (1) is most relevant when developing a library as opposed to an application, and issue (2) can be resolved by ensuring the service module is only imported once to prevent multiple service instances.

The challenges associated with the providedIn syntax include:

  1. providedIn: 'root' severs the connection between the service and its parent module, leading to potential bundling complexities where dependencies are concerned. This places the burden on the service consumer to manage injectable dependencies, which can be confusing and counterintuitive.
  2. A circular reference dilemma emerges if the service is utilized by components within the same module, as preserving the link becomes impossible via this syntax.

While going against official Angular recommendations, my suggestion would be: Avoid using providedIn unless you are creating a third-party library requiring tree-shaking capabilities. Instead, opt for the conventional providers syntax within the module like so:

@NgModule({ providers: [MyService], })

Answer №3

This issue is not related to Angular dependencies.

The circular reference arises from the TypeScript compiler encountering circular imports.

A Possible Solution

To tackle this, consider creating a separate module called ProtectedResolversModule. Then, use

providedIn: ProtectedResolversModule
and place the resolvers there.

You can then import this new module into ProtectedModule, which should help eliminate the circular dependency error when loading ProtectedRoutingModule.

Another Approach

An alternative solution could involve utilizing the providers array within ProtectedModule.

Answer №4

When working with Angular9+

You have the option to utilize providerIn: Any

Essentially, it functions similarly to a module, but without direct module usage to avoid circular dependency issues.

Documentation: https://angular.io/api/core/Injectable#options

'any' : Provides a distinct instance in each lazily loaded module, while all eagerly loaded modules share a single instance.

In essence, it operates within a separate injection tree. It differs from instances used in other modules.

For further references

https://dev.to/christiankohler/improved-dependeny-injection-with-the-new-providedin-scopes-any-and-platform-30bb

'Any' is beneficial for ensuring that a service remains a singleton within module boundaries, serving as a strong alternative to 'root' to prevent cross-module side effects.

Sample code snippet for providerIn

  private injectableDefInScope(def: ɵɵInjectableDef<any>): boolean {
    if (!def.providedIn) {
      return false;
    } else if (typeof def.providedIn === 'string') {
      return def.providedIn === 'any' || (def.providedIn === this.scope);
    } else {
      return this.injectorDefTypes.has(def.providedIn);
    }
  }

Answer №5

Explore the forwardRef() function in angular/core. This function enables referencing items that have not yet been defined.

import {AnotherService} from './another-service';

constructor(@Inject(forwardRef(() => AnotherService)) public anotherService: AnotherService) {
}

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

Learn the steps for implementing i18n-x in Angular 2 to localize constant property values

I am currently working on localizing my Angular2 application using the i18n-x form from here. It has been successful for attributes like so: <p-dialog i18n-header header="User Details"></p-dialog> The result is: <trans-unit id="fe871da89f ...

The variable 'React' is defined but not utilized in the code

Here's the code snippet in question: // tslint:disable import * as React from 'react'; import { Input, InputProps } from '../atoms/Input/Input'; import { FormControl } from '../hoc/FormControl/FormControl'; export const ...

What is the best way to refine React Component's props with Typescript?

My setup involves utilizing two specific components: Test and Subtest. The main functionality of the Test component is to provide visual enhancements and pass a portion of its props down to the Subtest component. Some props in the Subtest component are des ...

Unveiling the Ultimate Method to Package Angular 2 Application using SystemJS and SystemJS-Builder

I'm currently in the process of developing an application and I am faced with a challenge of optimizing the performance of Angular 2 by improving the loading speed of all the scripts. However, I have encountered an error that is hindering my progress: ...

In what ways can enhancing the TypeScript type system with additional restrictions help eliminate errors?

Encountered issues while working on my TypeScript project due to errors in the type definitions of a library. The solution was to enable the strictNullChecks flag. It seems counter-intuitive that adding restrictions can eliminate errors, when usually it&a ...

What situations call for the use of 'import * as' in TypeScript?

Attempting to construct a cognitive framework for understanding the functionality of import * as Blah. Take, for instance: import * as StackTrace from 'stacktrace-js'; How does this operation function and in what scenarios should we utilize imp ...

Implementing asynchronous image loading with Angular 4 using bearer headers

Currently, I am working on making asynchronous image requests with authentication headers. The image paths in the code look like this: <img src="{{file.src}}"/> I need to include a Bearer Token in the header for these requests, but since there are ...

An error was encountered: The CommonModule type does not contain the 'ɵmod' property. This issue is occurring at the getNgModuleDef function in the core.js file

After transferring my project to Amazon Workspace and updating Angular to version 14.2.6, I encountered an issue where I received the error message: "Uncaught Error: Type CommonModule does not have 'ɵmod' property." This error did not occur on m ...

Can Angular Flex support multiple sticky columns at once?

I am trying to make the information columns in my angular material table stay sticky on the left side. I have attempted to use the "sticky" tag on each column, but it did not work as expected. <table mat-table [dataSource]="dataSource" matSort class= ...

Angular 2 child route causing application to become unresponsive

Hey there, I'm currently working on setting up child routes for my Angular app and this is what I have so far: import {bootstrap} from 'angular2/platform/browser' import {CommercifyComponent} from './commercify.component' import { ...

Leverage the JSON Web Token module within a Chrome extension

Currently in the process of developing a chrome extension but encountering an issue with loading the json web token node module in my Node.js setup. background-script.ts import jwt from 'jsonwebtoken'; // import * as jwt from '../node_mod ...

What is the process for extracting components from a JSON file using an observable in Angular?

Take a look at this snippet of code: response: any; fetchData(url: any) { this.response = this.http.get(url); } ngOnInit(): void { fetchData("url.com/data.json"); console.log(this.response) } When I check the console, I see Obser ...

Add a new item to an array in Angular 2 when a click event occurs

I'm trying to add a new list item (which comes from an API) when a button is pressed, but I'm not sure how to do it. Can anyone provide some guidance? Here's the code: <ul> <li *ngFor="let joke of jokes">{{joke.value}}</li> ...

What is the correct regex expression for validating decimal numbers between 1.0 and 4.5?

I'm having trouble creating an expression to validate numbers between 1.0 to 4.5 accurately. The current expression I'm using is not working as intended: /^[1-4]{0,1}(?:[.]\d{1,2})?$/ The requirement is to only allow values between 1.0 to ...

Using Pydantic to define models with both fixed and additional fields based on a Dict[str, OtherModel], mirroring the TypeScript [key: string] approach

Referencing a similar question, the objective is to construct a TypeScript interface that resembles the following: interface ExpandedModel { fixed: number; [key: string]: OtherModel; } However, it is necessary to validate the OtherModel, so using the ...

Verify if all the words commence with a certain character using regular expressions

I am working on form validation using Angular, and I am trying to verify if all words in a string start with the letter 'Z'. I am implementing this using a pattern so that I can dynamically change the input's class. So far, this is what I h ...

How can the component prefix be replaced or updated in Angular 8/9?

Is there a way to efficiently replace or modify the outdated component prefix all at once? OR How can I create a dynamic prefix system that automatically updates all component prefixes whenever changes are needed? ...

Two-way data binding in Angular 2 is a powerful feature that allows for

My goal is to construct a parent component called Action, which includes two child components named Infos and Localisation. I want to connect the inputs of the children with the parent model. This is the model: export class Action{ title: string; ...

Determine in Typescript if a value is a string or not

In my code, I have a component: export type InputData = string | number | null; interface InputData { data?: string | number | null; validate: boolean; } const App: React.FC<InputData> = ({ data = '', validate = true, }) => ...

Generic parameter with a union type

The proxy function returns a randomly determined type. const numbersArray = [1,2,3,4]; const stringsArray = ['1','2','3','4']; function func<T>(array: T[]): T[][] { return [[array[0], array[1]], [array[2], ...