angular - 不需要的可观察服务订阅
问题描述
遵循缓存服务模式,我通过在各种组件中使用从它创建BehaviourSubject
的只读来使用订阅Observable
// credential.service.ts
private _credentialList$: BehaviorSubject<Credential[]>
readonly credentialListObv$: Observable<Credential[]>
...
this. credentialListObv$ = this.credentialList$.asObservable()
...
getCredentialList(...): Observable<Credential[]> {
return this.http.get<Credential[]>(
``
).pipe(
tap(credList => {
...
this._credentialList$.next(credList)
}),
)
}
因此,我从组件触发该getCredentialList
方法以按主题发出获取的列表_credentialList$
,因此所有订阅者都credentialListObv$
应该获得该值。
问题是,当我创建我的组件时,我会在调用该getCredentialList
方法时收到不需要的订阅
// credential-list.component.ts
private _credentials$: Observable<Credential[]>
constructor(
private credentialService: CredentialService,
) {
this._credentials$ = this.credentialService.credentialListObv$.pipe(
tap(() => {console.log("sub")}),
...
)
}
ngAfterViewInit(): void {
this.credentialService.getCredentialList(...).subscribe()
}
get credentials$() {
return this._credentials$
}
// credential-list.component.html
<mat-expansion-panel *ngFor="let credential of credentials$ | async; let i=index">
...
结果是当我访问该页面"sub"
时会打印两次。第一个订阅发生在组件模板内的管道中,但是如果我永远不会从组件中调用,async
为什么还有另一个订阅呢?subscribe
this._credentials$
credential-list
解决方案
那是因为您还订阅了返回 HTTP observable 的服务方法。所以现在你的组件有两个对一个 http 调用的有效订阅。
Angular 中的 HTTP 方法在发出一个值后完成,因此您可以安全地订阅方法内部的 observable。
getCredentialList(...): Observable<Credential[]> {
this.http.get<Credential[]>(
``
).pipe(
tap(credList => {
...
this._credentialList$.next(credList)
}),
).subscribe();
}
然后在你的组件中,让你的 observable 保持不变,但不要订阅该方法。
ngAfterViewInit(): void {
this.credentialService.getCredentialList(...);
}
现在调用 service 方法会触发 HTTP 请求,而您的私有_credentials$
observable 仍会从 BehaviorSubject 获取数据。
编辑:既然你说你不想订阅服务内的任何东西,我可以为你提供一个解决方案,只需要在组件模板中订阅一次。
// credential.service.ts
private credentialParams$ = new Subject<ApiParameters>();
public credentialListObv$: Observable<Credential[]>;
...
this.credentialListObv$ = this.credentialParams$
.pipe(
switchMap(urlParameters=>
this.http.get<Credential[]>(`url with ${urlParameters}`)
)
);
...
getCredentialList(newParams:ApiParameters): void {
this.credentialParams$.next(newParams);
}
使用主题发出 API 调用所需的任何参数。然后 observable 接受发出的参数并直接返回 HTTP 请求 observable。
// credential-list.component.ts
public credentials$: Observable<Credential[]>
constructor(
private credentialService: CredentialService,
) {
this.credentials$ = this.credentialService.credentialListObv$.pipe(
tap(() => {console.log("sub")}),
...
);
}
ngAfterViewInit(): void {
this.credentialService.getCredentialList(apiParams);
}
// credential-list.component.html
<mat-expansion-panel *ngFor="let credential of credentials$ | async; let i=index">
在组件中,我们只订阅了可观察的服务。在 init 生命周期中,我们调用 service 方法。这会导致您的组件 observable 对将从服务 observable 返回的 HTTP 请求做出反应。
PS 如果您知道 observable 完成(如 HTTP 请求),则可以订阅服务中的某些内容。
推荐阅读
- javascript - 针对 O365 (Azure AD??) 进行身份验证的 Chrome 扩展
- data-structures - 为关键字挖掘文本时,扫描列表 n 次或将列表的每个部分与 n 个字符串比较是否更快?
- linux - gem5 构建失败并显示“错误:'printPFflags' 未在此范围内声明”
- javascript - 如何查看 JavaScript 调用堆栈从顶部弹出
- java - 使用 Union Find 从 top-btm 查找路径
- office-js - Office.js Web WORD 加载项:window.open() 方法不适用于“about:blank”URL
- c++ - 在map擦除c ++期间执行循环多少次
- java - 当您可以使用扫描仪时,为什么 DataOutputStream 与 FileWriter 有任何不同?
- java - 如何处理用 null 初始化的构造函数?
- java - android studio中奇怪的运行时错误