在我的Angular 2应用程序中,我有两个相互依赖的服务(服务A来自服务B的调用方法,反之亦然)。
以下是相关代码:
app.component.ts
:
import {Component} from 'angular2/core';
import {TempService} from '../services/tmp';
import {Temp2Service} from '../services/tmp2';
@Component({
selector: 'my-app',
templateUrl: 'app/app/app.component.html',
providers: [TempService, Temp2Service]
})
export class AppComponent { (...) }
服务1:
import {Injectable} from 'angular2/core';
import {Temp2Service} from './tmp2';
@Injectable()
export class TempService {
constructor (private _sessionService: Temp2Service) {}
}
服务2:
import {Injectable} from 'angular2/core';
import {TempService} from './tmp';
@Injectable()
export class Temp2Service {
constructor (private _sessionService: TempService) {}
}
运行该应用程序会导致以下错误:
EXCEPTION:无法解析'Temp2Service'的所有参数(未定义)。确保所有参数都使用Inject进行修饰或具有有效的类型注释,并且'Temp2Service'使用Injectable进行修饰
在其中一个服务中评论构造函数时,应用程序运行正常。所以我的猜测是两个服务的“交叉引用”导致了这个问题。
你知道这里出了什么问题吗?或者我的方法已经错了?
这是一种称为循环依赖的。这不是Angular2本身的问题。我所知道的任何语言都不允许这样做。
您将需要重构代码以删除此循环依赖项。您可能需要将其中一项服务拆分为新服务。
如果您遵循单一责任原则,您将发现您不会进入循环依赖陷阱。
如果不能使用单例,您可以尝试在其中一项服务上调用NEW。喜欢
this._sessionService = new TempService(this);
这是我采用的方法,因为这两种服务都没有使用未定义的成员变量。
我试图用setTimeout修复循环依赖性警告或使用注入器并将注入从构造函数移动到另一个函数的所有东西对于我来说都不适用于角度7。
这是我的工作解决方案:
我创建了另一个服务只是为了保存对第一个服务的服务引用:
@Injectable()
export class AnotherService {
private _service: AService;
get service(): AService {
return this._service;
}
set service(service: AService) {
this._service = service;
}
}
然后我可以像这样使用它:
@Injectable()
export class AService {
constructor(private anotherService: AnotherService) {
anotherService.service = this;
}
...
}
和这里:
@Injectable()
export class BService {
private aService: AService;
constructor(private injector: Injector) {
const anotherService = injector.get(AnotherService);
this.aService = anotherService.service;
}
...
}
构造函数注入可防止循环依赖。
可以通过注入Injector
并强制请求依赖关系来解决它:
private payrollService:PayrollService;
constructor(/*private payrollService:PayrollService*/ injector:Injector) {
setTimeout(() => this.payrollService = injector.get(PayrollService));
}
这里的关键不是通过构造函数注入服务,而是使用显式setter和getter。我会在Angular 4中使用以下模式:
app.component.ts
import { FooService } from './foo/foo.service';
import { BarService } from './bar/bar.service';
export class AppComponent {
constructor(public fooService: FooService, public barService: BarService) {
this.fooService.setBarService(barService);
}
}
foo.service.ts
@Injectable()
export class FooService {
barService: any;
constructor(){
}
setBarService(barService: any): void {
this.barService = barService;
}
getBarService(): any {
return this.barService;
}
}
Angular 2文档中有一章关于循环依赖。我觉得非常有用。
我更新了此解决方案以使用Angular> 4.使用Injector类,您可以将服务注入另一个服务
import { Injector } from '@angular/core';
import { TempService } from './tmp';
@Injectable()
export class Temp2Service {
private tempService: any;
constructor (private injector: Injector) { }
public funcA() {
this.tempService = this.injector.get(TempService);
this.tempService.doSomething();
}
}
这是一个循环依赖,不幸的是它是一个基本的计算机科学问题,或信息问题,Angular无法解决的问题。尝试做这样的事情:
export class ServiceA{
constructor(private b: ServiceB){
b.setA(this);
}
}
export class ServiceB {
private a: ServiceA
constructor(){
}
setA(a){
this.a = a;
}
}
这可能是最好的方法。
如果您正在使用Angular 2,并且在某些事件上需要循环依赖关系来调用彼此的函数,您可以使用Observables并在Service中订阅它们,其中您注入了其他服务。
小例子: -
@Injectable()
class Service1{
observeEvents(){
return Obsevable.create((o)=>{
//store `o` it in any class variable
//whenever you want to call function of Service2 from this class, do this `o.next('method_name');`
});
}
}
@Injectable()
class Service2{
constructor(private service1: Service1){
this.service1.subscribe((method)=>{
this[method]();
});
}
}
我们可以解决forwordRef函数来解决这个问题。
//允许引用尚未定义的引用。
@Inject(forwardRef(()=> MyService))private httpProxy:MyService