自动初始化 BehaviorSubject 缓存全局数据
Auto-initialize BehaviorSubject to cache global data
简化用例:
- 我有 Angular 具有多个模块的应用程序,大多数(不是全部)模块使用机场列表
- 我想创建一个
global-cache.service.ts
并将机场列表缓存在 BehaviorSubject
中,它将作为 Observable 公开。我只想在用户登陆订阅该 Observable 的组件时初始化 BehaviorSubject
(点击数据库)(与在服务控制器中初始化它相比)
这是起点,无法正常工作(请参阅 this.getAirpotsFromDB()
调用后的评论):
全局-cache.service.ts
airports: BehaviorSubject<string[]> = new BehaviorSubject(null);
get airports$(): Observable<string[]> {
if (this.airports.getValue() == null) {
//get list from db, and initialize the subject
this.getAirportsFromDB();
//**PROBLEM:** how do I return `this.airports.asObservable()` after it's initialized with data from the call above or return it from inside the call?
}
else{
//list initialized, emit from subject (don't hit db)
return this.airports.asObservable();
}
}
getAirportsFromDB() {
this.http.get<string[]>('/api/airports').subscribe((_result) => {
this.airports.next(_result);
});
}
myComponent1.ts
//imports global-cache.service but never subscribes to airports$ (service has many other arrays that are used here)
//User lands here first, `getAirportsFromDB()` never gets called, user goes to myComponent2
myComponent2.ts
...
airports$: any = this.globalCacheService.airports$;
//subscribed in html `airports$ | async`
...
//user lands here, `getAirportsFromDB()` is called, `airports` BehaviorSubject is initialized
//user then goes to myComponent3
myComponent3.ts
...
airports$: any = this.globalCacheService.airports$;
//subscribed in html `airports$ | async`
...
//`airports` BehaviorSubject emits last value (initialized in Component2). `getAirportsFromDB()` does not get called again
在 get airports()
中,您应该始终 return 可观察的。如果数据库 API 尚未被调用,则调用 getAirportsFromDB
,就像您当前正在做的那样
由订阅 returned 可观察对象的组件检查 returned 数据并基于此更新它们的视图(这将是异步的)
Angular 大学网站有一些 detailed examples 的服务也可以帮助
您可以尝试类似下面的操作,只需使用 tap
运算符并将 http 请求的值保存在 BehaviorSubject
和 return Observable
中 getAirportsFromDB
export class Service {
airports: BehaviorSubject<string[]> = new BehaviorSubject(null);
get airports$(): Observable<string[]> {
return this.airports.getValue()
? this.airports.asObservable()
: this.getAirportsFromDB();
}
getAirportsFromDB(): Observable<string[]> {
return this.http.get<string[]>('/api/airports')
.pipe(
tap((_result) => this.airports.next(_result))
)
}
}
次要更新:
BehaviorSubject observable 没有在第一次调用时得到 returned,所以后续调用 getter 不会更新视图,所以你可以使用上面的代码,以防万一,你将它用作缓存并且不想接收任何更新
最后选择了 shareReplay(1)
;方法更简单,并且完全按照我的意愿行事
global-cache.service.ts
airports$: Observable<string[]> = this.getAirportsFromDB();
getAirportsFromDB() {
return this.http.get<string[]>('/api/airports').pipe(shareReplay(1));
}
简化用例:
- 我有 Angular 具有多个模块的应用程序,大多数(不是全部)模块使用机场列表
- 我想创建一个
global-cache.service.ts
并将机场列表缓存在BehaviorSubject
中,它将作为 Observable 公开。我只想在用户登陆订阅该 Observable 的组件时初始化BehaviorSubject
(点击数据库)(与在服务控制器中初始化它相比)
这是起点,无法正常工作(请参阅 this.getAirpotsFromDB()
调用后的评论):
全局-cache.service.ts
airports: BehaviorSubject<string[]> = new BehaviorSubject(null);
get airports$(): Observable<string[]> {
if (this.airports.getValue() == null) {
//get list from db, and initialize the subject
this.getAirportsFromDB();
//**PROBLEM:** how do I return `this.airports.asObservable()` after it's initialized with data from the call above or return it from inside the call?
}
else{
//list initialized, emit from subject (don't hit db)
return this.airports.asObservable();
}
}
getAirportsFromDB() {
this.http.get<string[]>('/api/airports').subscribe((_result) => {
this.airports.next(_result);
});
}
myComponent1.ts
//imports global-cache.service but never subscribes to airports$ (service has many other arrays that are used here)
//User lands here first, `getAirportsFromDB()` never gets called, user goes to myComponent2
myComponent2.ts
...
airports$: any = this.globalCacheService.airports$;
//subscribed in html `airports$ | async`
...
//user lands here, `getAirportsFromDB()` is called, `airports` BehaviorSubject is initialized
//user then goes to myComponent3
myComponent3.ts
...
airports$: any = this.globalCacheService.airports$;
//subscribed in html `airports$ | async`
...
//`airports` BehaviorSubject emits last value (initialized in Component2). `getAirportsFromDB()` does not get called again
在 get airports()
中,您应该始终 return 可观察的。如果数据库 API 尚未被调用,则调用 getAirportsFromDB
,就像您当前正在做的那样
由订阅 returned 可观察对象的组件检查 returned 数据并基于此更新它们的视图(这将是异步的)
Angular 大学网站有一些 detailed examples 的服务也可以帮助
您可以尝试类似下面的操作,只需使用 tap
运算符并将 http 请求的值保存在 BehaviorSubject
和 return Observable
中 getAirportsFromDB
export class Service {
airports: BehaviorSubject<string[]> = new BehaviorSubject(null);
get airports$(): Observable<string[]> {
return this.airports.getValue()
? this.airports.asObservable()
: this.getAirportsFromDB();
}
getAirportsFromDB(): Observable<string[]> {
return this.http.get<string[]>('/api/airports')
.pipe(
tap((_result) => this.airports.next(_result))
)
}
}
次要更新: BehaviorSubject observable 没有在第一次调用时得到 returned,所以后续调用 getter 不会更新视图,所以你可以使用上面的代码,以防万一,你将它用作缓存并且不想接收任何更新
最后选择了 shareReplay(1)
;方法更简单,并且完全按照我的意愿行事
global-cache.service.ts
airports$: Observable<string[]> = this.getAirportsFromDB();
getAirportsFromDB() {
return this.http.get<string[]>('/api/airports').pipe(shareReplay(1));
}