1

*** - Hi guys, I've been having a problem for days. I am trying to populate an object with the result of a query to a JSON API. I need to fill in a model because through it I need to nail a key to make another query in another api and return the data on the screen. But so far all I can get is undefined

To better understand I need to fill the generation Object so that through it I can fill the data of another object and get a url to query another endpoint api and return other data from the screen.



export class PokeApp implements OnInit  {

   gen1: Generation[];
   gen2: Generation[];

    generation : Generation;

  constructor(private http: HttpClient, private gService:GenerationService) {

  }

  ngOnInit(){
   this.getGeneration1();
   this.getGeneration2();
   // Only for test, this is not the data ho i need - but this object generation returns null, i do not now how.
   this.gService.getPokemon_Species(this.generation.name);
  }

// this request return a gen1 object to my screen, but a need this object in JS code
// to do another query.
  getGeneration1(): void{
    this.gService.getGeneration1().subscribe(gen =>{

    this.gen1 = gen
    this.generation = gen[0];
  });

  }

  getGeneration2(): void{
    this.gService.getGeneration2().subscribe(gen => this.gen2 = gen);

    console.log("Still Returned Undefined___>>>>" + this.generation);
 }

// this do the request to a Poke API

export class GenerationService {
  private GetGenerationURL1 = "https://pokeapi.co/api/v2/generation/1";
  private GetGenerationURL2 = "https://pokeapi.co/api/v2/generation/2";

  httpOptions = { headers: new HttpHeaders({ "Content-Type": "application/json" }) };


  constructor(private http: HttpClient) { }

  getGeneration1(): Observable<Generation[]> {
    return this.http.get<Generation[]>(this.GetGenerationURL1)
      .pipe(
        tap(_ => console.log('fetched generations')),
        catchError(this.handleError<Generation[]>('getGeneration1', []))
      );
    // Subscribe to begin listening for async result
  }

  getGeneration2(): Observable<Generation[]> {
    return this.http.get<Generation[]>(this.GetGenerationURL2)
    .pipe(
      tap(_ => console.log('fetched generations')),
      catchError(this.handleError<Generation[]>('getGeneration2', []))
    );
  }

  getPokemon_Species(url: string): Observable<Pokemon[]> {
    console.log("___>>>>${generation}" + url);
    return this.http.get<Pokemon[]>(url)
      .pipe(
        tap(_ => console.log('fetched Species')),
        catchError(this.handleError<Pokemon[]>('getPokemon_Species', []))
      );
  }

  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

}

  • 1
    welcome to the asynchronous world, please read: https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call and https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular and probably you want to chain your requests depending on case with `switchMap`, `mergeMap` or `forkJoin` for example. – AT82 Oct 31 '19 at 17:38
  • Thanks, but it's not what I need yet. If you look at my code you will see that the way it is I can capitalize the object on the screen, though, it helped me a lot from your links. The point is that I need to populate a template with the API response. I can do this easily in java. But here is giving headache. – Daniel Batista Oct 31 '19 at 18:09
  • I'm getting confused here. So what exactly is the issue? You are not showing any template. – AT82 Oct 31 '19 at 18:33

1 Answers1

1

Update

So the issue actually is with the typings. You don't need to add the [] after the the Generation anywhere. As there isn't any place that the API will respond with an Array of Generations.

So remove the [] from the returning type of getGeneration1 and in the typed response of the HTTP in the service.

Please note that the typings in Typescript are only for compiling time, it doesn't affect anything in the runtime, just to make sure you are using the right references and detect errors before runtime.

I'm adding the getGeneration functions here:

getGeneration1(): Observable<Generation> {
  return this.http.get<Generation>(this.GetGenerationURL1)
    .pipe(
      tap(_ => console.log('fetched generations')),
      catchError(this.handleError<Generation>('getGeneration1', []))
    );
}

getGeneration2(): Observable<Generation> {
  return this.http.get<Generation>(this.GetGenerationURL2)
  .pipe(
    tap(_ => console.log('fetched generations')),
    catchError(this.handleError<Generation>('getGeneration2', []))
  );
}

In the component, you will need to refactor it like this:

export class PokeApp implements OnInit  {

gen1: Generation;
gen2: Generation;
generation : Generation;

constructor(private http: HttpClient, private gService:GenerationService) {

}

ngOnInit(){
  this.getGeneration1();
  this.getGeneration2();
  this.gService.getPokemon_Species(this.generation.name);
}

getGeneration1(): void{
    this.gService.getGeneration1().subscribe(gen =>{
      this.gen1 = gen
      this.generation = gen;
  });

}

getGeneration2(): void{
    this.gService.getGeneration2().subscribe(gen => this.gen2 = gen);
}

This is in case you still need your code in the component to work without chaining the responses as I provided in the old answer, but I suggest to refactor your code same as this:

getGenerations() {
  this.gService.getGeneration1()
    .pipe(mergeMap(gen => {
      this.generation = gen;
      return this.gService.getGeneration2();
    }))
    .pipe(mergeMap(gen => {
      return this.gService.getPokemon_Species(this.generation.name);
    }))
    .subscribe(response => console.log(response));
}

Old Answer

You well need to use mergeMap. It should be something like this:

getGenerations() {
  this.gService.getGeneration1()
    .pipe(mergeMap(gen => {
      this.gen1 = gen;
      this.generation = gen[0];
      return this.gService.getGeneration2();
    }))
    .pipe(mergeMap(gen => {
      this.gen2 = gen;
      return this.gService.getPokemon_Species(this.generation.name);
    }))
    .subscribe(response => console.log(response));
}
yazantahhan
  • 2,950
  • 1
  • 13
  • 16
  • I believe I was not clear on what I need and so I apologize. You can completely ignore the getGeneration2 method because the same procedure I must do for both methods. The idea is that the getGeneration1 and 2 method return me a Generation object, which contains this information: abilities: ArrayType; name: string; id: number; main_region: ArrayType; moves: ArrayType; names: ArrayType; pokemon_species: ArrayType; types: ArrayType; version_groups: ArrayType; Alas through this data I consult the species of Pokemon. – Daniel Batista Oct 31 '19 at 18:17
  • I'm sorry Daniel, I'm still not sure I understand the issue. When returning the generation, do oyu need to show it or use something in it in the next request? – yazantahhan Oct 31 '19 at 18:36
  • i need something like this : generation : Generation; this.gService.getGeneration1().subscribe(response =>{ this.gen1= response this.generation = this.gen1; // (But it is an array) // OR this.generation = this.gen1[0]; // This return Null console.log("I need this name ->" +this.generation.name); // return undefined. }); – Daniel Batista Oct 31 '19 at 18:58
  • this case return to me ERROR TypeError: Cannot read property 'name' of undefined – Daniel Batista Oct 31 '19 at 19:04
  • So the issue is that the response of 0 `response[0]` of the getGeneration1 function is undefined right? But as I understood, the getGeneration1 returns an object, so why you can't just make `this.generation = gen` so the name property will be their? – yazantahhan Oct 31 '19 at 20:05
  • because of this : Type 'Generation[]' is missing the following properties from type 'Generation': abilities, name, id, main_region, and 5 more.ts(2740). How i can cast this? – Daniel Batista Oct 31 '19 at 20:11
  • Oh it is a typing issue. Please check my update answer – yazantahhan Oct 31 '19 at 20:36