首页 > 解决方案 > 如何在 Angular 中使用 Observable 获取多个页面?

问题描述

所以我尝试了很多方法,但没有发现。欢迎大家帮忙;我发现了 Angular 和 RxJs。

我有一项服务可以在 swapi API 的许多 URL 上获取资源。我事先不知道会获取多少页。所以我用concatfor eachhttp.get(url)来创建一个可观察的。
目前,只有第一页数据被添加到组件中(即firstPage);所有请求都已发送。

export class PeoplesService {
  urlSource = "https://swapi.co/api/people/";
  pageResponse: GeneralResponse<People>;
  fullResponse: Observable<GeneralResponse<People>>;

  constructor(private _http:HttpClient) {
  }

  getPaged(n: number): string {
    return this.urlSource + "?page=" + n;
  }

  fetch(): Observable<GeneralResponse<People>> {
    let firstPage = this._http
                      .get<GeneralResponse<People>>(this.urlSource);
    firstPage.subscribe(page => {
      this.fullResponse = firstPage; // first page fetched
      let pageToDownload = Math.ceil(page.count / page.results.length);
      for(let i=2; i<=pageToDownload; i++) {
        // Merge all observable (so all request) into one
        concat(this.fullResponse,
               this._http.get<GeneralResponse<People>>(this.getPaged(i)));
      }
    });
    return this.fullResponse;
  }
}

然后我的组件的基本代码如下:

  ngOnInit() {
    this.peoplesService.fetch().subscribe(r => this.movies = r.results);
    // a sort of fetch().onNextFetch(this.movies.push(...r.results)) seems better in this case
    // because every data on each pages need to be merged into this.movies
    // or sort of fetch().subscribeUntilCompleted(r => this.peoples = r.results) needed I guess
  }

我还没有找到可以用来代替订阅的东西(例如当 Observable 已返回所有内容并收集所有内容时的订阅......)。

我猜 subscribe 不会等待 Observable 的“onCompleted”状态,并且不会每次都调用它来获取所有返回值。那么如何获取所有数据呢?

有什么东西可以让 Observable 像流一样工作并将其通过管道传输到this.peoples.push(...r.results)?我不知道我是否走在正确的轨道上。

标签: angulartypescriptrxjs

解决方案


好的,经过几个小时的工作,我找到了答案。我用来mergeMap()从第一个 Oservable 获取数据并将merge()所有 Observable 合并为一个。

import { HttpClient } from '@angular/common/http';
import { Observable, merge } from 'rxjs';
import { take, map} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class PeoplesService {

  private getPaged(n: number): string {
    return this.urlSource + "?page=" + n;
  }

  /**
   * Return an array with the natural number in it.
   * @param size : size
   * @param start : first number
   * Examples : 
   *    naturalNumberArrayFactory(3) returns [1, 2, 3]
   *    naturalNumberArrayFactory(3, 2) returns [3, 4, 5]
   */
  protected static naturalNumberArrayFactory(n: number, start = 1): number[]{
    return [ ...Array(n).keys() ].map(x => x + start);
  }

  fetch(): Observable<GeneralResponse<People>> {
    return this._http
      .get<GeneralResponse<T>>(this.urlSource)
      .pipe(take(1), map(firstPage => {
        // Amount of page to get ( -1 because got firstPage)
        let pageToDownload = Math.ceil(firstPage.count / firstPage.results.length) - 1;
        // Generate array ids and replace them with GET requests
        let observables = GenericService.naturalNumberArrayFactory(pageToDownload, 2)
          .map(id => this._http.get<GeneralResponse<T>>(this.getPaged(id)));
        // Add the first page to avoid to repeat the request
        observables.unshift(of(firstPage));
        return merge(...observables);
      }));
  }
}

推荐阅读