Subscribe to observable is returning undefined
Asked Answered
I

7

17

So I am trying to subscribe to a simple service that return data from a local JSON file.

I have managed to get the service working, I can log it out in the function, but when I subscribe to the service in the angular 2 component, it is always undefined. I'm not sure why? Any help would be much appreciated.

API service

export class ApiService {
   public data: any;

   constructor(private _http: Http) {
   }

   getData(): any {
       return this._http.get('api.json').map((response: Response) => { 
       console.log('in response', response.json()); //This logs the Object
       this.data = response.json();
       return this.data;
   })
   .catch(this.handleError);
   }
}

Component

export class AppComponent {
   public data: any
   public informationData;

   constructor(private _api: ApiService) {}

   public ngOnInit(): void {
      console.log(this.getDataFromService()); // This return undefined
   }

   public getDataFromService() {
      this._api.getData().subscribe(response => {
          this.informationData = response;
          return this.informationData;
      });
   }
}
Islington answered 16/10, 2017 at 11:16 Comment(8)
getDataFromService doesn't itself return anything. Change .subscribe to .map and return the resulting observable, then use | async to resolve it in the template.Daggett
A subscribe function doesnt return the data. It returns a Subscription object. i.e if you return the function call itself.Owensby
@SurajRao Yes, I noticed this, how do I get it to return the response data?Islington
where do you want to return it to?Owensby
you can save it in class variable within subscribe if that is what you wantOwensby
@SurajRao is that not what I have done already in the subscribe with this.informationData = response;, return this.informationData;?Islington
why return this.informationData; its why I asked why you need to return from subscribe?Owensby
So just remove return this.informationData; as it is already been assigned to the variable? Thank you for all thisIslington
O
68

Maybe some pictures help?

The numbers here indicate the order of operations.

Send the Http Request enter image description here

  1. Component is initialized and calls the getMovies method of the movieService.
  2. The movieService getMovies method returns an Observable. NOT the data at this point.
  3. The component calls subscribe on the returned Observable.
  4. The get request is submitted to the server for processing.
  5. The ngOnInit method is complete.

Any code here after the subscribe cannot access the movies property since the data has not yet been returned.

Receive the Http Response enter image description here

At some LATER point in time ...

  1. The movies are returned to the service.
  2. If the process was successful, the first callback function is executed.
  3. The local movies property is assigned to the movies returned from the service. It is only here that the movies property is finally set.

Attempting to access the movies property prior to step #8 results in an error.

Can we access the value here? NO enter image description here

To fix it: enter image description here

Onlybegotten answered 17/10, 2017 at 4:49 Comment(6)
After few days of searching answer I found your post. Thank your very much Deborah! That's what I am looking for. But could you tell me how to parse/manipulate data from this.movies? I need to collect this and show in chart using chart.js/ ng chartLianna
This was spectacularly well written and demonstrated. Thank you @DeborahK!Vaientina
This did not seem to do the trick. Calling this.movies.length, still did not populate my html: <li *ngFor="let m of movies">{{m.name}}</li> results in a list of <li _ngcontent-c4></li> with no names. even adding {{m.name|async}} did not do the trick.Threadgill
That was not the point of this example. Calling this.movies.length will not cause the list to populate. Calling the .subscribe and setting this.movies = movies causes the this.movies to populate. This post was trying to show why you can't get the length outside of the observable. Try posting a new question and showing some of your code. Tag me and I'll take a look.Onlybegotten
What a fantastic explanation! Anyone struggling with asynch should take a look at this reply.Underpainting
To prove DeborahK's point you can add delay just below the this.movieService.getMovies().subscribe(.....); await new Promise(f => setTimeout(f, 1000)); And now check the length or content.Judithjuditha
M
0
objResponse;
this.service.getData().subscribe((result: any)=> { 
this.objResponse=result;
}

Returning something won't required

Moten answered 17/10, 2017 at 7:13 Comment(0)
B
0

you can do it like this:

In your app-component:

public getDataFromService() {
  this._api.getData(this);
}


public setData(data: any){
 this.data=data;
}

In your service/api.ts:

public getData(obj: appComponentModel){
    this.http.get(url).subscribe(res => obj.setData(res));
}
Belldame answered 16/2, 2020 at 0:49 Comment(0)
A
-1

Try with:

getData(): any {
       return this._http.get('api.json');
}

or

   getData(): any {
           return this._http.get('api.json').map((response: Response) => { 
    response.json();
})
Acetylene answered 16/10, 2017 at 11:24 Comment(0)
L
-1

You've got a problem between sync and async function. You'r issue is: getDateFromService is syncronous and the content inside is async. So when the ngOnInit function call getDataFromService, you'r code don't wait the async task. you'r getDataFromService need to return an observer or need to implement the return of your API (you need to choose).

public ngOnInit(): void {
  console.log(this.getDataFromService().subscribe(data => console.log(data)); // This return undefined
}

public getDataFromService() {
  return this._api.getData();
}
Lumpkin answered 16/10, 2017 at 11:26 Comment(2)
This works well, however when I try and add the data to a class variable it then still return undefinedIslington
Where do you want to get the response of your api? Because your problem here is you use sync/async functions. If in your class you have undefined, it's probably that your class don't wait the async call response before doing a console.logLumpkin
B
-1

Instead of logging at the ngOnInit() method as you did

 public ngOnInit(): void {
      console.log(this.getDataFromService()); // This return undefined    }

log inside the subscribe() method as

export class AppComponent {
  public data: any
  public informationData;

  constructor(private _api: ApiService) {}

  public ngOnInit(): void {
    this.getDataFromService(); //don't log here, logging here will return undefined
  }

  public getDataFromService() {
    this._api.getData().subscribe(response => {
      this.informationData = response;
      console.log(this.informationData); //log here, like this
      return this.informationData;
    });
  }
}
Belabor answered 3/2, 2019 at 2:2 Comment(0)
T
-2

Imagine 'subscribe' as a separate thread running, write everything that is needed inside an anonymous function inside 'subscribe'. Whenever the 'data' is available, it will be available inside the subscribe method.

Hope this helps.

Toenail answered 14/2, 2021 at 5:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.