Daniel Batista
Daniel Batista

Reputation: 39

Populating an Object Model in Angular 7

*** - 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);
    };
  }

}

Upvotes: 1

Views: 309

Answers (1)

yazantahhan
yazantahhan

Reputation: 3110

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));
}

Upvotes: 1

Related Questions