Angular 8:提取到另一个服务时,BehaviorSubject 部分不起作用
Angular8: BehaviourSubject partially not working when extracting to another Service
我遇到了一个非常奇怪的问题:在我的应用程序中,我遵循了让状态服务处理所有状态的原则。随着应用程序的增长,我决定创建子状态服务。我将所有相关功能提取到专用服务中。其中,这部分从 state.service.ts 移动到 bubble03-state.service.ts:
@Injectable({
providedIn: 'root'
})
export class Bubble03StateService {
constructor() { }
/* tslint:disable:rule1 member-ordering */
// ...
public currentVisibleEFSubcontainer$ = new BehaviorSubject<string>('');
private currentSubjectValue = '';
updateCurrentVisibleEFSubcontainer(newVisibleContainer: string): void {
console.log(`updating with value ${newVisibleContainer}`);
if (newVisibleContainer !== this.currentSubjectValue) {
this.currentVisibleEFSubcontainer$.next(newVisibleContainer);
this.currentSubjectValue = newVisibleContainer;
} else {
this.currentVisibleEFSubcontainer$.next('');
this.currentSubjectValue = '';
}
}
}
从两个位置调用方法 updateCurrentVisibleEFSubcontainer()
,一个是组件,一个是另一个服务。现在奇怪的是:一旦我将此代码移动到新的子状态服务,只有一个调用触发订阅,而另一个调用则不会。我真的无法解释为什么,因为它的代码完全相同,只是在另一个地方。所有其他移动的功能都可以正常工作。在这两个电话之后:
在组件中
onClickRowElement(): void {
if (this.rowElementAmount > 0) {
this.bubble03State.updateCurrentVisibleEFSubcontainer(this.rowElementTitle);
this.rowElementClicked.emit(this.rowElementTitle);
}
}
在另一个服务中
chart.options.data[0].click = (e) => {
this.explodePie(e);
this.bubble03State.updateCurrentVisibleEFSubcontainer(e.dataPoint.name);
};
订阅
this.bubble03State.currentVisibleEFSubcontainer$.subscribe(
newVisibleEFContainer => {
this.handleExpandableSubContainer(newVisibleEFContainer);
console.log(`newly visible ${newVisibleEFContainer}`);
}
);
如果从组件中多次调用该方法,则会记录以下输出
> updating with value Kontoführung
> newly visible Kontoführung updating
> with value Versand & Bearbeitung
> newly visible Versand & Bearbeitung
> updating with value Versand & Bearbeitung
> newly visible
但是,从服务调用方法会产生以下日志
> updating with value Kontoführung
> updating with value Versand & Bearbeitung
> updating with value Buchungsgebühren
只要我将方法及其相应的 BehaviourSubject 放回主服务中,一切都会按预期进行。什么可能导致这种行为?状态服务之间没有区别,除了名称。两者都在 root 中提供。
编辑:
为了澄清,发布问题时的初始情况如下:
StateService
位于 CoreModule
中并在 root
中提供。 CoreModule
仅在 AppModule
中导入。
Bubble03StateService
位于延迟加载的模块中,也在 root
中提供。
-
Bubble03StateService
用于延迟加载模块和第三个服务,它也位于 CoreModule
中并在 root
中提供,而 StateService
在每个模块中使用。
按照 Richard Dunn 的建议,我重新定位了 Bubble03StateService
并尝试了一些东西。它现在也位于 CoreModule
。
- 当我删除
provideIn: 'root'
并将两个服务都放在 AppModule
的 providers
数组中时,常规 StateService
仍然可以正常工作,但是 Bubble03StateService
抛出一个 NullInjectorError: no providers for...
- 重新插入
provideIn: 'root'
并从 AppModule
中删除,服务已加载,但发生了一些我不明白的事情:在服务的构造函数中,我创建了一个随机数并在任何时候记录它调用服务的功能。当把BehaviourSubject
及其对应的更新方法放在StateService
中时,随机数总是相同的,无论是谁调用该方法(第三方服务和延迟加载组件)。但是当我把它放回 Bubble03StateService
时(此时它在所有方面都与 StateService
相同,除了它的名字),随机数在从第三个服务和从延迟加载的组件。这让我更加困惑,因为现在来自 CoreModule
的所有三个服务都应该是单例的。如果不是,为什么 StateService
是单例而 Bubble03StateService
不是,当它们以相同的方式声明、提供和定位时?
可能是 Angular 8 的延迟加载模块导致了这个问题。具体来说,如果您使用 @Injectable({ providedIn: 'root' })
依赖注入方法,它可能会创建单例服务,具体取决于订阅者父模块与根模块的关系。
您应该删除此方法并改用:@NgModule({ providers: [Bubble03StateService] })
在您的根模块中。这将保证它是一个单例服务。
我终于找到问题的原因了。
不知何故,在其他服务中自动导入该服务时,自动导入功能将扩展名 .js
添加到导入语句中。这真的很难捕捉到,因为您通常不会明确检查它。
虽然此位置甚至没有 .js
文件,但 Angular 以某种方式理解这一点并从 [=12= 导入正确的 class / 服务 / 组件等]-文件。但是它创建了一个新的服务实例,破坏了单例的原则。
所以最后,只需从导入中删除 .js
扩展即可。
我遇到了一个非常奇怪的问题:在我的应用程序中,我遵循了让状态服务处理所有状态的原则。随着应用程序的增长,我决定创建子状态服务。我将所有相关功能提取到专用服务中。其中,这部分从 state.service.ts 移动到 bubble03-state.service.ts:
@Injectable({
providedIn: 'root'
})
export class Bubble03StateService {
constructor() { }
/* tslint:disable:rule1 member-ordering */
// ...
public currentVisibleEFSubcontainer$ = new BehaviorSubject<string>('');
private currentSubjectValue = '';
updateCurrentVisibleEFSubcontainer(newVisibleContainer: string): void {
console.log(`updating with value ${newVisibleContainer}`);
if (newVisibleContainer !== this.currentSubjectValue) {
this.currentVisibleEFSubcontainer$.next(newVisibleContainer);
this.currentSubjectValue = newVisibleContainer;
} else {
this.currentVisibleEFSubcontainer$.next('');
this.currentSubjectValue = '';
}
}
}
从两个位置调用方法 updateCurrentVisibleEFSubcontainer()
,一个是组件,一个是另一个服务。现在奇怪的是:一旦我将此代码移动到新的子状态服务,只有一个调用触发订阅,而另一个调用则不会。我真的无法解释为什么,因为它的代码完全相同,只是在另一个地方。所有其他移动的功能都可以正常工作。在这两个电话之后:
在组件中
onClickRowElement(): void {
if (this.rowElementAmount > 0) {
this.bubble03State.updateCurrentVisibleEFSubcontainer(this.rowElementTitle);
this.rowElementClicked.emit(this.rowElementTitle);
}
}
在另一个服务中
chart.options.data[0].click = (e) => {
this.explodePie(e);
this.bubble03State.updateCurrentVisibleEFSubcontainer(e.dataPoint.name);
};
订阅
this.bubble03State.currentVisibleEFSubcontainer$.subscribe(
newVisibleEFContainer => {
this.handleExpandableSubContainer(newVisibleEFContainer);
console.log(`newly visible ${newVisibleEFContainer}`);
}
);
如果从组件中多次调用该方法,则会记录以下输出
> updating with value Kontoführung
> newly visible Kontoführung updating
> with value Versand & Bearbeitung
> newly visible Versand & Bearbeitung
> updating with value Versand & Bearbeitung
> newly visible
但是,从服务调用方法会产生以下日志
> updating with value Kontoführung
> updating with value Versand & Bearbeitung
> updating with value Buchungsgebühren
只要我将方法及其相应的 BehaviourSubject 放回主服务中,一切都会按预期进行。什么可能导致这种行为?状态服务之间没有区别,除了名称。两者都在 root 中提供。
编辑:
为了澄清,发布问题时的初始情况如下:
StateService
位于CoreModule
中并在root
中提供。CoreModule
仅在AppModule
中导入。Bubble03StateService
位于延迟加载的模块中,也在root
中提供。-
Bubble03StateService
用于延迟加载模块和第三个服务,它也位于CoreModule
中并在root
中提供,而StateService
在每个模块中使用。
按照 Richard Dunn 的建议,我重新定位了 Bubble03StateService
并尝试了一些东西。它现在也位于 CoreModule
。
- 当我删除
provideIn: 'root'
并将两个服务都放在AppModule
的providers
数组中时,常规StateService
仍然可以正常工作,但是Bubble03StateService
抛出一个NullInjectorError: no providers for...
- 重新插入
provideIn: 'root'
并从AppModule
中删除,服务已加载,但发生了一些我不明白的事情:在服务的构造函数中,我创建了一个随机数并在任何时候记录它调用服务的功能。当把BehaviourSubject
及其对应的更新方法放在StateService
中时,随机数总是相同的,无论是谁调用该方法(第三方服务和延迟加载组件)。但是当我把它放回Bubble03StateService
时(此时它在所有方面都与StateService
相同,除了它的名字),随机数在从第三个服务和从延迟加载的组件。这让我更加困惑,因为现在来自CoreModule
的所有三个服务都应该是单例的。如果不是,为什么StateService
是单例而Bubble03StateService
不是,当它们以相同的方式声明、提供和定位时?
可能是 Angular 8 的延迟加载模块导致了这个问题。具体来说,如果您使用 @Injectable({ providedIn: 'root' })
依赖注入方法,它可能会创建单例服务,具体取决于订阅者父模块与根模块的关系。
您应该删除此方法并改用:@NgModule({ providers: [Bubble03StateService] })
在您的根模块中。这将保证它是一个单例服务。
我终于找到问题的原因了。
不知何故,在其他服务中自动导入该服务时,自动导入功能将扩展名 .js
添加到导入语句中。这真的很难捕捉到,因为您通常不会明确检查它。
虽然此位置甚至没有 .js
文件,但 Angular 以某种方式理解这一点并从 [=12= 导入正确的 class / 服务 / 组件等]-文件。但是它创建了一个新的服务实例,破坏了单例的原则。
所以最后,只需从导入中删除 .js
扩展即可。