如何在 Angular HttpClient 中根据多个可能的触发器进行 API 调用,而无需在我的代码中多次订阅 API 调用方法?

How to make an API call in Angular HttpClient upon multiple possible triggers without subscribing to the API call method multiple times in my code?

我正在制作一个稍大的 Angular Cli 应用程序,我有一个数据 table,它有很多动态过滤器和一个搜索栏,基本上和任何时候:

A) The search value changes
B) The pagination changes
C) A filter is removed or applied

我需要进行 api 调用以获取更新的 table 项目和更新的过滤器信息。

我的服务中有一个功能,returns api 调用的响应看起来像这样...

getFilters() {
    const body = { searchTerm: 'foo' }; // 
    return http.post(url, body)
        .map(res => res)
        .catch(err => Observable.of(err);
}

getTableItems() {
    const body = { searchTerm: 'foo', ...stuff for filtering }; // 
    return http.post(url, body)
        .map(res => res)
        .catch(err => Observable.of(err);
}

getApiData() {
    const filters = this.getFilters();
    const items = this.getTableItems();
    return forkJoin([ filters, items ]);
}

我还有很多其他方法会被调用,就像我说的,当分页发生变化或新的搜索词等时...基本上我必须将 getApiData().subscribe(res => doSomethingWithResponse(res)); 放在构造函数,以及需要像这样触发它的每个方法

constructor() {
    ...constructor stuff;
    this.getApiData().subscribe(res => ...);
}
setPageSize() {
    ...stuff
    this.getApiData().subscribe(res => ...);
}
setPage() {
    ...stuff
    this.getApiData().subscribe(res => ...);
}
setSSearchTerm() {
    ...stuff
    this.getApiData().subscribe(res => ...);
}
... and so on...

我的问题是,我怎样才能保持这个 DRY 并且仍然在多个触发器上进行 API 调用?有办法处理这个问题吗?请注意,我还使用 @ngrx/store(Redux 实现)在前端存储数据,并且我在我的代码中使用了 rxjs ObservableBehaviorSubject

来自您 API 的帖子是您应用程序中的一个数据源。触发事件来源:

  1. 搜索值
  2. 分页
  3. 过滤器

让我们从打印组件中的帖子开始。会有一些 posts$ 可观察到的 - 帖子来源。

<div *ngFor="let post in posts$ | async">{{post}}</div>

让我们在您的组件中定义此类可观察对象。

posts$: Observable<Post>;
ngOnInit() {
    const body = {};
    this.posts$ = http.post(url, body);
}

不错,但我们没有使用任何参数,对吧?

那么我们的变化来源是什么?让我们将它们定义为可观察对象。

posts$: Observable<Post>;
page$: Observable<number>;
searchTerm$: Observable<string>;
otherFilters$: Observable<Filters>;

现在让我们在 ngOnInit() 钩子中定义我们的事件源。为了简单起见,我将只关注分页和搜索输入。

ngOnInit() {
    // page is probably defined by URL
    this.page$ = this.route.paramMap
        .switchMap((params: ParamMap) => params.get('page'));
    // search term and filters will be probably from some form
    this.searchTerm$ = this.searchTerm.valueChanges
        .startWith('');

    // nothing changed here for now...
    const body = {};
    this.posts$ = http.post(url, body);
}

好的,现在我们希望 page$$searchTerm 的任何更改都可以触发新数据下载。让我们根据 page$searchTerm$.

定义我们的 posts$ observable
this.posts$ = Observable.combineLatest(this.page$, this.searchTerm$, (page, searchTerm) => ({ page, searchTerm}))
    .switchMap(params => {
        let body = params;
        return http.post(url, body);
    });

这里注意combineLatest的最后一个参数。它是 transform 函数,每次发出某些东西时都会调用它,每个可观察对象的值作为参数。我只是创建了一个对象,稍后我将在 switchMap 参数中收到它。

这就是您可以组合可观察对象并订阅最终数据源的方法。当您使用 async 管道进行订阅时,它会处理自动取消订阅,因此您不必手动管理它。希望有所帮助。