0

I'm coding an Angular front-end, in which I'd like to gather all HTTP calls to the back-end in the same service.

The failing commit is here (with just one change for it to work), that I summarize herebelow.

So, I've got this BackendService class:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { catchError, timeout } from 'rxjs/operators';

const backendUrl = 'http://localhost:5001/api/';
const statusEndpoint = '/status';

@Injectable({
  providedIn: 'root'
})
export class BackendService {
  // exposed Subject of this service
  public status$ = new BehaviorSubject<BackendStatus>(defaultStatus);

  constructor(
    private http: HttpClient,
  ) { }

  private updateStatus(): void {
    this.get(statusEndpoint).subscribe(raw => { this.status$.next(raw); });
  }

  public get(endpoint: string): Observable<HttpResponse<any>> {
    return this.http.get(backendUrl + endpoint);
  }

(...)

So far, so good. Now I'd like to have other services rely on the BackendService.get method, which would be the central place to take care of timeouts, error handling, and such other stuff.

Now, when I define this service in another service, like this:

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

import { BackendService } from './backend.service';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(
    private backend: BackendService,  // <-- here!
  ) { }

Then compilation goes without error, but I get following console error:

ERROR Error: Cannot instantiate cyclic dependency! AuthService
    Angular 7
    UserPanelComponent_Factory user-panel.component.ts:12
    Angular 5
        getNodeInjectable
        instantiateAllDirectives
        createDirectivesInstances
        elementStart
        element
    AppComponent_Template app.component.html:4
    Angular 20
core.js:6241:19

Where user-panel.component.ts is importing AuthService:

import { Component, OnInit } from '@angular/core';

import { AuthService, Credentials } from '../auth.service';
import { UserService, UserDetails } from '../user.service';


@Component({
  selector: 'app-user-panel',
  templateUrl: './user-panel.component.html',
  styleUrls: ['./user-panel.component.scss']
})
export class UserPanelComponent implements OnInit {
  public userDetails: UserDetails;

  constructor(
    public auth: AuthService,
    public user: UserService,
  ) {}

  ngOnInit(): void {
    // trigger refresh from local storage once the component is ready
    this.auth.initializeFromStorage();
  }

  onLogOut() {
    this.auth.logUserOut();
  }

}

So question: how can I import a service into another service?

Comments:

  • I saw other questions about real cyclic dependencies, but here I really stripped my example down, and there are no other dependencies in my services. I also couldn't find anything close on internet.

  • I feel it may be related to the content of @Injectable, though emptying it did not bring visible improvement.)

Joël
  • 2,723
  • 18
  • 36
  • 1
    Please, add example to git. In your case there is no circular dependency. By the way, do all subscriptions in components and do not forget to unsubscribe – Юрий Безруков Nov 13 '20 at 13:22
  • 1
    You should show more code. For instance how you've defined the `UserPanelComponent`. Like the previous commenter mentioned, a stackblitz reproducing your issue would be best. I can see this happening if you have the following in your `providers` array: `{ provide: BackendService, useClass: AuthService }` – Poul Kruijt Nov 13 '20 at 13:38
  • Mmh, I must admit I over-simplified the example, to try to be clear; I just tried to reproduce on a minimal example, but couldn't trigger the failure. Here is the [failing commit](https://framagit.org/ojob/je-gere/-/commit/ea60af749457c3480a3b1a9a7b48b1dff930660a), sorry it's on the whole projet, but the commit diff will help showing the triggering change. – Joël Nov 13 '20 at 13:46
  • @ЮрийБезруков It would seem relevant (to me at least :-)) that the services make things easy to the components for automated start of the `Subject`. What place would you consider relevant, for a central trigger of such subscriptions? – Joël Nov 13 '20 at 14:02
  • I just clarified, by pushing a commit with the sole change that avoids the error: https://framagit.org/ojob/je-gere/-/commit/e6c76bbc8c89cee502628aa6828360a75778a0e9 – Joël Nov 13 '20 at 14:06
  • I moved the subscriptions out of the service... and now, no complaint anymore! Thanks @ЮрийБезруков for the hint :-) Once this is clear in my mind, I'll update my question, so as to capitalize for others. – Joël Nov 13 '20 at 17:46

1 Answers1

0

The "clasic" is use Injector to get a reference to a new BackendService

private backend: BackendService
constructor (injector:Injector) {
    this.backend = injector.get(BackendService);
}
Eliseo
  • 50,109
  • 4
  • 29
  • 67