【问题标题】:Angular: How to use a Service to fetch data from server and store in an array?Angular:如何使用服务从服务器获取数据并存储在数组中?
【发布时间】:2020-10-04 07:34:49
【问题描述】:

我有一个服务可以在https://pokeapi.co/api/v2/pokemon/ 调用PokéAPI

如果您没有指定要返回的 Pokemon,API 将返回一个分页的资源列表,其中包含 Pokemon 的 URL。 https://pokeapi.co/docs/v2#pokemon-section

我想将该 URL 列表存储在我的服务的本地数组中。

我试过了:

getListOfPokemonUrls(): Observable<any> {
    return this.http.get<any>(this.pokeApiUrl)
      .pipe(
        map((response: any[]) => this.pokemonResources = response.results)
      );
  }

在我的构造函数中(用于测试目的):

const y = this.getListOfPokemonUrls();

但是,当我跳过该函数时,this.pokemonResources 数组为空。

【问题讨论】:

  • 希望您已完成 y.subscribe 以调用 API。由于 api 调用是异步的,因此跨步函数不会填充所需的数组。您应该在地图运算符中放置一个断点。
  • @user2216584 即使这样做:``` const y = this.getListOfPokemonUrls(); y.订阅(); ```如果我链接 .subscribe - this.getListOfPokemonUrls().subscribe();,我的本地数组 this.pokemonResources 仍然是空的,y 是不可观察的类型订阅者,但在我的数组中存储数据仍然没有运气

标签: angular rxjs angular-http


【解决方案1】:

要点

  • 使用 observables 时,一定要subscribe()
  • 与其将服务器响应存储在本地变量中,不如返回数据,以便订阅 Observable 的调用者可以决定如何处理数据。这样做的好处是,一旦异步过程完成,数据就会立即可用。

查看工作Stackblitz Demo

StackBlitz 演示的注意事项

  1. 创建了一个 Angular 服务 PokeAPIService,它在构造函数中调用 PokeAPI 服务器。
  constructor(private http: HttpClient) {
    this.getListOfPokemonUrls().subscribe(
      (results: Array<Pokemon>) => {
        for(let p of results) {
          this.pokemons.push(p)
        }
      }
    )
  }

请注意,getListOfPokemonURLs 的更新实现会返回结果,以便可以在对 subscribe() 的调用中对其进行处理。

  1. PokemonPokeAPIResponse 定义接口:
export interface Pokemon {
  name: string,
  url: string
}


interface PokeAPIResponse {
  count: number,
  next: string,
  previous: string,
  results: Array<Pokemon>
}
  1. 用新的接口类型更新了getListOfPokemonUrls(),并修改了RxJS map,以便直接返回response.results
 private getListOfPokemonUrls(): Observable<Array<Pokemon>> {
    return this.http.get<any>(POKEAPI_URL)
      .pipe(
        map((response: PokeAPIResponse) => response.results)
      );
  }

作为参考,PokeAPI 服务器响应:

curl https://pokeapi.co/api/v2/pokemon/

返回以下响应:

{
  "count":1050,
  "next":"https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20",
  "previous":null,
  "results":[
    {"name":"bulbasaur","url":"https://pokeapi.co/api/v2/pokemon/1/"},
    {"name":"ivysaur","url":"https://pokeapi.co/api/v2/pokemon/2/"},
    {"name":"venusaur","url":"https://pokeapi.co/api/v2/pokemon/3/"}, 
    {"name":"charmander","url":"https://pokeapi.co/api/v2/pokemon/4/"}, 
    {"name":"charmeleon","url":"https://pokeapi.co/api/v2/pokemon/5/"}, 
    {"name":"charizard","url":"https://pokeapi.co/api/v2/pokemon/6/"}, 
    {"name":"squirtle","url":"https://pokeapi.co/api/v2/pokemon/7/"},
...

【讨论】:

  • 是的,所以如果我在管道函数中进行控制台记录,我会得到所需的数据。我尝试订阅方法调用,但数组 this.pokemonResources 仍然是空的...我应该声明我是 angular 新手。
  • 感谢@Christopher Peisert 的示例!有趣的是,您的代码和我的代码之间的区别在于,我试图完全在 Angular 服务中执行它,而不使用组件。原因是,我想遍历 url,然后在分页列表中显示 pokemon 数据。所以我必须有一个方法循环遍历每个 url 并将其映射到我的 Pokemon 界面。尝试完全在服务中执行此操作会成为问题吗?我已将您的代码复制到服务中,但我的数组仍然是空的。实际上现在它是未定义的,但初始化它会使其为空。
  • 一种解决方案是在服务的constructor 方法中调用this.getListOfPokemonUrls().subscribe()。第一次注入服务时,将获取数据。或者,this.getListOfPokemonUrls().subscribe() 可以放在服务中的方法中,以便服务的消费者根据需要调用。
  • 是的,这正是我一直在尝试做的。似乎将它放在服务的构造函数中似乎并没有达到我的预期。我能想到完成这项工作的唯一方法是订阅组件,让组件获取它想要的数据并将其发送回服务以检索单个口袋妖怪?那个听起来是对的吗?我知道 Angular 是固执己见的。这最初对我来说像是一种反模式,但我已经把它分成 2 个服务,Pokemon/Resources 和 2 个组件 Pokemon/Resources... 似乎正在做我需要的。
  • @Mossi92 查看更新的 StackBlitz 演示,它使用 Angular 服务来获取 Pokemon 数据。干杯!
猜你喜欢
  • 1970-01-01
  • 2020-09-08
  • 2017-11-15
  • 2023-03-03
  • 2018-03-27
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多