2
votes

I'm making an HTTP call in a service & assigning the return data to a service variable. Now when I try to access that service variable in a component, it logs as undefined in console. However it gets logged when I put the log code in service itself but it doesn't work when in component.

Below is my code for reference:

hero.service

import { Injectable }              from '@angular/core';
import { Http, Response }          from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Hero } from './hero';

@Injectable()
export class HeroService {
heroes: Hero[];
hero: Hero;
gbl: Hero[];

  private heroesUrl = 'SERVICE URL';
  constructor (private http: Http) {}

  getHeroes(): Observable<Hero[]> {
    return this.http.get(this.heroesUrl)
                    .map(this.extractData)
                    .catch(this.handleError);
  }
  private extractData(res: Response) {
    let body = res.json()['data'];
    this.gbl = body;
    return body || { };

  }
  private handleError (error: Response | any) {
    Handles Error
  }

getHero(id: number): Observable<Hero> {
    return this.getHeroes()
      .map(heroes => heroes.find(hero => hero.id == +id));
  }
}

hero-list.component

import { Component } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { HeroService } from './hero.service';
import { Hero } from './hero';

@Component({
  template: `Template`
})

export class HeroListComponent {
  errorMessage: string;
  heroes: Hero[];
  listlcl: Hero[];
  id: number;
  public listheroes: Hero[];
  mode = 'Observable';
  private selectedId: number;

  constructor (
  private heroService: HeroService,
  private route: ActivatedRoute,
  private router: Router
  ) {}

  ngOnInit() { this.getHeroes() }
  getHeroes() {
  this.id = this.route.snapshot.params['id'];
  console.log(this.id);
    this.heroService.getHeroes()
                     .subscribe(
                       heroes => {
                       this.heroes = heroes;
                       this.listlcl = this.heroService.gbl;
                       console.log(this.listlcl);
                       },
                       error =>  this.errorMessage = <any>error);
  }

  isSelected(hero: Hero) { return hero.id === this.id; }

  onSelect(hero: Hero) {
    this.router.navigate(['/hero', hero.id]);
  }
}
1
Why do you need "gbl" (which is not a great name, by the way) at all, given that you have access to the same data in the subscription as heroes, the callback parameter? Could you expand on "doesn't work"? - jonrsharpe
@jonrsharpe, You'r correct that heroes has the same data. I'm actually trying to access the service variable in component. My intention is to make single HTTP call in service & use the returned data in multiple component (list / detail) by using service variable. I tried doing some changes but it didn't work as I'm a newbie in angular2. - Jamshed
In that case I'd recommend trying a different approach; sharing an observable of the data you've collected. I actually wrote this up for my colleagues recently: blog.jonrshar.pe/2017/Apr/09/async-angular-data.html - jonrsharpe
@JayakrishnanGounder, I've shared it as seen in my code above. I'm able to access the service observable but not the service variable. - Jamshed

1 Answers

5
votes

Issue with your code is when you use extractData inside map this variable is not the instance of your service, it is assigned to the mapper of http request.

you can simply convert your function to be an Arrow function, so that scope is set to service instance like below, and you would be able to see the variable value in component which is now correctly assigned to service instance.

private extractData = (res: Response) => {
    let body = res.json()['data'];
    this.gbl = body;
    return body || { };    
  }

Check this Plunker!!

Hope this helps!!

some reference, taken from typescript documentation,

this and arrow functions

In JavaScript, this is a variable that’s set when a function is called. This makes it a very powerful and flexible feature, but it comes at the cost of always having to know about the context that a function is executing in. This is notoriously confusing, especially when returning a function or passing a function as an argument.

We can fix this by making sure the function is bound to the correct this before we return the function to be used later. This way, regardless of how it’s later used, it will still be able to see the original deck object. To do this, we change the function expression to use the ECMAScript 6 arrow syntax. Arrow functions capture the this where the function is created rather than where it is invoked