1

I am a beginner with Angular and TypeScript, so I am creating my own application using the public Pokemon API for practice. However, I want some feedback on the task to use http to get information on the pokemons with ids between [1, 50]. To get the Pokemon for each id, I need a separate http GET request. As a result, I have 50 Observable objects that I need to subscribe to and then generate an array out of the results from all 50 Observables. However, I want some advice on if there exists a better way to achieve what I am hoping to.

poke-dashboard.component.ts (Code to subscribe to 50 Observables)

import { Component, OnInit } from '@angular/core';
import { PokemonService } from '../services/pokemon.service';
import { Pokemon } from '../shared/pokemon';

@Component({
  selector: 'app-poke-dashboard',
  templateUrl: './poke-dashboard.component.html',
  styleUrls: ['./poke-dashboard.component.scss'],
})
export class PokeDashboardComponent implements OnInit {
  pokemons: Pokemon[];

  constructor(private pokemonservice: PokemonService) {}

  ngOnInit(): void {
    let pokemonsList: Pokemon[] = [];
    for (var i = 1; i <= 50; i++) {
      this.pokemonservice.getPokemonForId(i).subscribe((data: any) => {
        pokemonsList.push(this.pokemonservice.generatePokemon(data));
      });
    }
    this.pokemons = pokemonsList;
  }
}

pokemon.service.ts (Code to handle http calls)

import { Injectable } from '@angular/core';
import { Pokemon } from '../shared/pokemon';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class PokemonService {
  baseUrl: string;
  private pokemons: Pokemon[] = [
    new Pokemon(
      'pikachu',
      'pikachu',
      90,
      50,
      50,
      40,
      55,
      35,
      'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png'
    ),
  ];
  constructor(private httpClient: HttpClient) {
    this.baseUrl = 'https://pokeapi.co/api/v2/';
  }

  public getPokemonForId(id: number): Observable<any> {
    return this.httpClient.get(this.baseUrl + `pokemon/${id}`);
  }

  public generatePokemon(pokeinfo: any): Pokemon {
    return new Pokemon(
      pokeinfo['name'],
      pokeinfo['species']['name'],
      pokeinfo['stats'][0]['base_stat'],
      pokeinfo['stats'][1]['base_stat'],
      pokeinfo['stats'][2]['base_stat'],
      pokeinfo['stats'][3]['base_stat'],
      pokeinfo['stats'][4]['base_stat'],
      pokeinfo['stats'][5]['base_stat'],
      pokeinfo['sprites']['front_default']
    );
  }

  public getPokemons() {
    return this.pokemons;
  }
}
1
  • Could you be more specific about what "a better way" means? Just cleaner code, or do you want to send several requests at a time, or limit the number of requests that you want to send before having a response? Commented Apr 26, 2020 at 17:07

1 Answer 1

2

If you starting with Angular and RxJS, I wouldn't recommend PokeAPI to learn it with. It is a little complicated to obtain information from.

Neverthless, it contains hypertext media in the result. Instead of using a for loop, you could obtain URLs for each ID using this hypertext media. A GET request to the URL https://pokeapi.co/api/v2/pokemon/?limit=3&offset=0 fetches the following result.

{
  "count": 964,
  "next": "https://pokeapi.co/api/v2/pokemon/?offset=3&limit=3",
  "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/"
    }
  ]
}

You could make a request with parameter limit=50 if you need the first 50 entries. Now we could obtain all the URLs and make the HTTP requests. Instead of subscribing to it individually, we could use RxJS forkJoin which allows us to subscribe to multiple observables.

If you are starting with Angular, it might a little too much. I'd say to go at it one thing at a time. I'd recommend you to start with the Angular Tutorial. It introduces the basics.

Working example: Stackblitz

pokemon-api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';

import { of, forkJoin } from 'rxjs';
import { map, catchError, tap } from 'rxjs/operators';

export const BASE_URL = 'https://pokeapi.co/api/v2';

@Injectable()
export class PokemonApiService {

  constructor(private httpClient: HttpClient) { }

  public getListOfPokemon(limit: string, offset ?: string) {
    const params = new HttpParams()
      .set('offset', offset ? offset : '0')
      .set('limit', limit);
    return this.httpClient
      .get<any>(`${BASE_URL}/pokemon`, { observe: 'response', params: params })
      .pipe(
        map(res => res.body.results),
        catchError(error => of(error.url))
      );
  }

  public getPokemonDetails(urlList: Array<any>) {
    urlList = urlList.map(url => this.httpClient.get<any>(url));
    return forkJoin(urlList);
  }
}

app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, catchError, tap } from 'rxjs/operators';

import { PokemonApiService } from './pokemon-api.service';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  pokemons = [];
  limit = '50';

  constructor(private _pokemonApiService: PokemonApiService) {}

  ngOnInit(){
    this.getPokemonList();
  }

  private getPokemonList() {
    this._pokemonApiService.getListOfPokemon(this.limit).subscribe(
      response => { this.getPokemonDetails(response.map(response => response.url)); },
      error => { console.error(error); }
    );
  }

  private getPokemonDetails(urlList: Array<string>) {
    this._pokemonApiService.getPokemonDetails(urlList).subscribe(
      response => { this.pokemons = response; },
      error => { console.error(error); }
    );
  }

}

app.component.html

<table>
  <tr>
    <th>ID</th>
    <th>Name</th>
    <th>Sprite</th>
  </tr>
  <tr *ngFor="let pokemon of pokemons">
    <td>{{ pokemon.id }}</td>
    <td>{{ pokemon.name | titlecase }}</td>
    <td><img [src]="pokemon.sprites.front_default"/></td>
  </tr>
</table>
Sign up to request clarification or add additional context in comments.

Comments

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.