Is it possible to inject a service inside another

2020-04-04 15:38发布

问题:

I have a real scenario in a real project where I need 2 services to access the properties and/or methods of each other. I'm not an Angular expert so is it possible?

I have tried and it fails. Here's my attempt:

app.component.ts

import { Component } from '@angular/core';
import { FirstService } from './first.service';
import { SecondService } from './second.service';

@Component({
  selector: 'my-app',
  template: '<h1>Hello world!</h1>',
  providers: [FirstService, SecondService]
})
export class AppComponent {

  constructor(public firstService: FirstService, public secondService: SecondService) {
    console.log(firstService.foo);
    console.log(secondService.bar);
  }

}

first.service.ts

import { Injectable } from '@angular/core';
import { SecondService } from './second.service';

@Injectable()
export class FirstService {

  foo: string = 'abc';

  constructor(public secondService: SecondService) {
    this.foo = this.foo + this.secondService.bar;
  }

}

second.service.ts

import { Injectable } from '@angular/core';
import { FirstService } from './first.service';

@Injectable()
export class SecondService {

  bar: string = 'xyz';

  constructor(public firstService: FirstService) {
    this.bar = this.bar + this.firstService.foo;
  }

}

Plunker: http://plnkr.co/edit/PQ7Uw1WHpvzPRf6yyLFd?p=preview

Just injecting the second service into the first service works fine but as soon as I inject the first service into the second service it fails and throws errors to the console.

So what is wrong?

A working solution should print the following to the console log:

abcxyz
xyzabc

Thanks in advance!

回答1:

AngularJS does not allow injection of circular dependencies.

Miško Hevery, one of the authors of AngularJS, recommends finding the common elements:

+---------+      +---------+
|    A    |<-----|  B      |
|         |      |  |  +-+ |
|         |      |  +->|C| |
|         |------+---->| | |
|         |      |     +-+ |
+---------+      +---------+

And extracting it to a third service:

                         +---------+
+---------+              |    B    |
|    A    |<-------------|         |
|         |              |         |
|         |    +---+     |         |
|         |--->| C |<----|         |
|         |    +---+     +---------+
+---------+

For more information, see Circular Dependency in constructors and Dependency Injection by Miško Hevery.



回答2:

I'm not an Angular expert so is it possible

No. Circular dependencies are not resolved by angular's DI.

Also even systems that do support it, quite commonly are inconsistent e.g. commonjs https://nodejs.org/docs/latest/api/modules.html#modules_cycles will give you an empty object for a while.

Solution

Consider combining the two services into one. You can still move certain stuff (e.g. simple functions etc) out from the combined service if it becomes too much.



回答3:

I agree with the solution proposed by basarat. Another workaround would be to initialize the instances outside DI and provide them as value like

One service needs to be modified to be able to create an instance without providing the other service as dependency:

@Injectable()
export class FirstService {

  foo: string = 'abc';
  secondService: SecondService

  constructor() {
    //this.foo = this.foo + this.secondService.bar;
  }

  init(secondService:SecondService) {
    this.foo = this.foo + secondService.bar;
  }
}

Then create the instances imperatively and provide them as value

let firstService = new FirstService();
let secondService = new SecondService(firstService);

@Component({
  selector: 'my-app',
  template: '<h1>Hello world!</h1>',
  providers: [
    provide(FirstService, {useFactory: () => {
      firstService.init(secondService);
      return firstService;
  }}), provide(SecondService, {useValue: secondService})]
})
...

Plunker example