Paul Hulme
Paul Hulme

Reputation: 93

Accessing data returned from HTTP Observable

I am trying to use data returned from an Angular Observable Service in a function outside the subscription. I can successfully access the data in the View Component (.html), however when I am unable to access the data from another function in the component.ts file. The value is returned as 'undefined' when trying to access from the 'drawChart' function below.

How do I access the specialistCount data from the drawChart function?

My component code is as follows: dashboard.component.ts

  ngOnInit() {
        this.getUserCounts();
        this.drawCharts();
 }

     getUserCounts() {
        this.userService.getUserCount('all').subscribe(totalUserCount => { this.totalUserCount = totalUserCount;  });
        this.userService.getUserCount('specialist').subscribe(specialistCount => { this.specialistCount = specialistCount; });
        this.userService.getUserCount('delegate').subscribe(delegateCount => { this.delegateCount = delegateCount; });
        this.userService.getUserCount('practioner').subscribe(practionerCount => { this.practionerCount = practionerCount; });
        this.userService.getUserCount('admin').subscribe(adminCount => { this.adminCount = adminCount; });
     }

     drawCharts() {
         console.log('The valiue:' + this.specialistCount);
        this.pieChartData =  {
        chartType: 'PieChart',
        dataTable: [
          ['Users', 'Hours per Day'],
          ['Specialists', this.specialistCount],
          ['Delegates', this.delegateCount],
          ['practioners', 15],
          ['QMS Staff', 0]
        ],
        options: {'title': 'Users'},
      };
     }

My Service code is: user.service.ts

  getUserCount(role) {
    const headers = new Headers();
    this.loadToken();
    headers.append('Authorization', this.authToken);
    headers.append('Content-Type', 'application/json');
    return this.http.get(this.apiUrl + '/users/count/' + role, { headers: headers }).map(res => res.json());
  }

Upvotes: 0

Views: 240

Answers (2)

Paul Hulme
Paul Hulme

Reputation: 93

As per Lazar's answer I have modified my code to use a forkJoin and call the populateGrid function inside the Observable.

New code as follows:

ngOnInit() {
        this.getUserCounts();
 }

 getUserCounts() {
     forkJoin(this.userService.getUserCount('all'),
            this.userService.getUserCount('specialist'),
            this.userService.getUserCount('delegate'),
            this.userService.getUserCount('practioner'),
            this.userService.getUserCount('admin')
        ).subscribe(
            returnedValues => {
                this.totalUserCount = returnedValues[0];
                this.specialistCount = returnedValues[1];
                this.delegateCount = returnedValues[2];
                this.practionerCount = returnedValues[3];
                this.adminCount = returnedValues[4];
                this.drawCharts();
            }
        );
    }

 drawCharts() {
    this.pieChartData =  {
    chartType: 'PieChart',
    dataTable: [
      ['Users', 'Hours per Day'],
      ['Specialists', this.specialistCount],
      ['Delegates', this.delegateCount],
      ['Practioners', this.practionerCount],
      ['QMS Staff', this.adminCount]
    ],
    options: {'title': 'Users'},
  };
 }

Upvotes: 0

Lazar Ljubenović
Lazar Ljubenović

Reputation: 19764

I am trying to use data returned from an Angular Observable Service in a function outside the subscription.

You cannot do this. This is because the HTTP observables are asynchronous.

let foo
console.log('before', foo)
this.https.get('something').subscribe(result => {
  foo = result
  console.log('inside', foo)
})
console.log('after', foo)

Assuming that the observable returns a number 42 (result is 42), this will print the following:

before undefined
after undefined
inside 42

The first two lines would be printed immediately. The last line would be printed only after the browser sends the request to the server, the request travels to it through cables across the Earth, the response arrives and is parsed as JSON.

You need to call your getUserCount method inside the subscribe method. Seems like you need to make all five requests before you have enough information to run the getUserCount function, so you'll need to wait for all five requests to complete to run it.

You can see here how to do parallel requests.

To do the requests in parallel (for requests which are not dependant of one another), use the forkJoin static operator.

return Observable.forkJoin(
  this.http.get('url/1'),
  this.http.get('url/2'),
)
  .subscribe(([res1, res2]) => {
    // res1 and res2 available after both requests are completed
  })

Note that this has nothing to do with Angular -- it's only about RxJS and about the asynchronous nature of HTTP requests.

Upvotes: 2

Related Questions