0

I have a service which calls the PokéAPI at https://pokeapi.co/api/v2/pokemon/.

If you don't specify the Pokemon you want returned, the API will return a paginated list of resources, which contains URLs for Pokemon. https://pokeapi.co/docs/v2#pokemon-section

I want to store that list of URLs inside a local array for my service.

I have tried:

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

In my constructor (for testing purposes):

const y = this.getListOfPokemonUrls();

However, when I step over that function this.pokemonResources array is empty.

2
  • Hope you have done y.subscribe to invoke the API. Since api call is async so step over function will not have the desired array filled. You should put a breakpoint inside map operator. Commented Oct 3, 2020 at 21:41
  • @user2216584 even when doing that: ``` const y = this.getListOfPokemonUrls(); y.subscribe(); ``` my local array this.pokemonResources is still empty if I chain .subscribe - this.getListOfPokemonUrls().subscribe();, y is a type subscriber not observable but still no luck in storing the data in my array Commented Oct 3, 2020 at 21:52

1 Answer 1

3

Key Points

  • When working with observables, be sure to subscribe().
  • Rather than storing the server response in a local variable, prefer to return the data so that the caller that subscribes to the Observable can decide how to process the data. This has the advantage that as soon as the asynchronous process completes, the data is immediately available.

See working Stackblitz Demo.

Notes on the StackBlitz Demo

  1. Created an Angular Service PokeAPIService that calls the PokeAPI server in the constructor.
  constructor(private http: HttpClient) {
    this.getListOfPokemonUrls().subscribe(
      (results: Array<Pokemon>) => {
        for(let p of results) {
          this.pokemons.push(p)
        }
      }
    )
  }

Note that the updated implementation of getListOfPokemonURLs returns the results so that they can be processed within the call to subscribe().

  1. Defined interfaces for Pokemon and PokeAPIResponse:
export interface Pokemon {
  name: string,
  url: string
}


interface PokeAPIResponse {
  count: number,
  next: string,
  previous: string,
  results: Array<Pokemon>
}
  1. Updated getListOfPokemonUrls() with new interface types and modified RxJS map so that response.results is returned directly.
 private getListOfPokemonUrls(): Observable<Array<Pokemon>> {
    return this.http.get<any>(POKEAPI_URL)
      .pipe(
        map((response: PokeAPIResponse) => response.results)
      );
  }

For reference, the PokeAPI server response:

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

Returns the following response:

{
  "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/"},
...
Sign up to request clarification or add additional context in comments.

7 Comments

Yep, so if I console log this inside the pipe function, I get the data desired. I have tried to subscribe to the method call, but the array this.pokemonResources is still empty... I should state that I'm new to angular.
Thank you for the example @Christopher Peisert! Interestingly, the difference between your code and mine, is that I'm trying to do it inside an angular service entirely, without using a component. The reason being, I want to loop through the urls then display pokemon data in a paginated list. So I'd have to have a method which loops over each url and map that to my Pokemon interface. Would trying to do this entirely in a service be a problem? I've copied your code into a service and my array is still empty. Actually right now, it's undefined, but initialising it leaves it empty.
One solution is to call this.getListOfPokemonUrls().subscribe() in the Service's constructor method. The first time the Service is injected, the data will be fetched. Alternatively, this.getListOfPokemonUrls().subscribe() could be placed in a method within the Service to be called as needed by consumers of the Service.
Yep this is exactly what I've been trying to do. It seems that putting this in the constructor of the service doesn't seem to be doing what I expect. The only way I can think of getting this work, is to subscribe in the component, let the component grab the data it wants and send that back up to the service to retrieve the individual Pokemon? Does that sound about right? I know Angular is meant to be opinionated. This initially felt like an anti pattern to me, but I've since broken it into 2 services, Pokemon/Resources and 2 components Pokemon/Resources... Seems to be doing what I need.
@Mossi92 See the updated StackBlitz demo, which uses an Angular Service to fetch the Pokemon data. Cheers!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.