在我的 Angular 服务中,我有一个缓存的 http 请求,它在我的组件初始化时被调用一次
//variables
private readonly URL: string = 'http://makeup-api.herokuapp.com/api/v1/products.json';
private cachingSubject: ReplaySubject<IProduct[]>
private _products: IProduct[] = [];
//method
public getAllProducts(): Observable<IProduct[]> {
if (!this.cachingSubject) {
this.cachingSubject = new ReplaySubject<IProduct[]>(1);
this.http.get<IProduct[]>(this.URL)
.subscribe((productsData: IProduct[]) => {
this._products = productsData;
this.cachingSubject.next(productsData);
});
}
return this.cachingSubject.asObservable();
}
和我的组件在创建服务方法时:
public products: IProduct[] = [];
private destroySubject$: Subject<void> = new Subject<void>();
ngOnInit(): void {
this.productService
.getAllProducts()
.pipe(takeUntil(this.destroySubject$))
.subscribe((products: IProduct[]) => {
this.products = products;
})
}
public onForceReload(): void {
//need to reload onClick
}
问题是,如何从缓存的请求中强制重新加载(重新初始化缓存)?再次获取 getAllProducts
最佳答案
我认为实现您正在寻找的最简单方法是使用单个主题作为触发器来获取数据。然后在服务上定义一个 allProducts$
可观察对象(而不是方法),它依赖于这个“获取主题”。
提供一个简单的 refreshProducts()
方法,该方法在获取主题上调用 .next()
,这将导致 allProduct$
重新获取数据。
消费者只需订阅 allProducts$
即可收到最新的产品系列。他们可以调用 refreshProducts()
来重新加载数据。无需重新分配对主题或可观察对象的引用。
export class ProductService {
private fetch$ = new BehaviorSubject<void>(undefined);
public allProducts$: Observable<IProduct[]> = this.fetch$.pipe(
exhaustMap(() => this.http.get<IProduct[]>('url')),
shareReplay(),
);
public refreshProducts() {
this.fetch$.next();
}
}
这是一个有效的 StackBlitz演示。
这是 allProducts$
的流程:
- 以
fetch$
开头,意味着它会在fetch$
发出时执行 -
exhaustMap
将订阅一个“内部可观察对象”并发出它的排放量。在这种情况下,内部可观察到的是 http 调用。 -
shareReplay
用于向新订阅者发出先前的发射,因此在调用refreshProducts()
之前不会进行 http 调用(如果您一次只有 1 个订阅者,则不需要这样做, 但一般在服务中,使用它是个好主意)
我们将 fetch$
定义为 BehaviorSubject
的原因是因为 allProducts$
在 fetch$
发出之前不会发出。 BehaviorSubject
最初会发出一个默认值,因此它会导致我们的 allProducts$
执行,而无需用户单击重新加载按钮。
请注意,服务中没有发生任何订阅。这是一件好事,因为它允许我们的数据是惰性的,这意味着我们不只是因为某些组件注入(inject)了服务而获取数据,我们只在有订阅者时才获取数据。
此外,这意味着我们的服务具有单向数据流,这使得调试起来容易得多。消费者只能通过订阅公共(public)可观察对象来获取数据,并且他们通过调用服务上的方法来修改数据……但是这些方法不会返回数据,只会导致数据被推送到可观察对象中。有一个真不错video Thomas Burleson(前 Angular 团队成员)就此主题进行了讨论。
你看,我为公开的可观察对象使用了名称 allProducts
而不是 getAllProducts
。由于 allProducts$
是可观察的,订阅行为意味着“获取”。
我喜欢将 observable 视为始终推送最新值的小数据源。当您订阅时,您正在倾听 future 的值(value),但消费者并没有“得到”它们。
我知道有很多,但我还有一点建议,我认为这些建议可以大大清理代码。
这是AsyncPipe
的用途.
因此您的组件代码可以简化为:
export class AppComponent {
public products$: Observable<IProduct[]> = this.productService.allProducts$;
constructor(private productService: ProductService) { }
public onForceReload(): void {
this.productService.refreshProducts();
}
}
还有你的模板:
<ul>
<li *ngFor="let product of products$ | async">{{ product }}</li>
</ul>
请注意,无需为了执行 this.products = products;
而在模板中订阅。异步管道为您订阅,更重要的是为您取消订阅。这意味着,您不再需要 destroy 对象了!
这是一个 StackBlitz之前更新的分支以使用异步管道。
关于Angular 2+缓存的HTTP请求强制重新加载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69145927/