user2301
user2301

Reputation: 1967

Angular Http response returned from the service is not accessible to class variable

I am having Angular 2 Typescript application. I want to access Build Artifacts from Jenkins server using Jenkins Rest API in my application. The Build Artifact conatins one text file. I want to read the text from the file. I am using http.get() from angular to access the jenkins url. The responseType returned is text() as the file contains some text. When I try to assign the response returned to the variable (this.data) defined in my component,I dont see any value assigned to it.

    //myservice.ts
    import { Http, Response } from '@angular/http';
    import { Injectable } from '@angular/core';
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/catch';
    import { AppConfig } from '../app.config';

    @Injectable()
    export class JenkinsService {
      private jenkinsRestAPI;
      constructor(private http: Http, private config: AppConfig) {
      }
      getTextFromJenkins(serviceName):Observable<string>{   
       this.jenkinsRestAPI= 'http://'+this.config.getConfig('jenkins')+'/job/'+serviceName
return this.http.get(this.jenkinsRestAPI)
          .map(this.extractData)
          .catch(this.handleError); 
      }

      // Extracts from response
      private extractData(res: Response) {
        if (res.status < 200 || res.status >= 300) {
          throw new Error('Bad response status: ' + res.status);
        }    
        let body = res.text();
        return body || ''; // here
      }
//mycomponent.ts
export class JenkinsComponent implements OnInit {
servicesList:Array<string> = ['Backend','DataService',
 'demoService','SystemService']
data:string;
errorMessage: string;
serviceName:string;

  constructor(private _jenkinsService: JenkinsService) {      
  }

  ngOnInit() {
      for(var i = 0; i < this.servicesList.length; i++){
            this.serviceName=this.servicesList[i];
          this.getValueFromJenkins(this.serviceName);
         // console.log(this.data);-- It shows undefined. No values seen

      } 
  }

 getValueFromJenkins(serviceName) {
    this._jenkinsService.getTextFromJenkins(this.serviceName).subscribe(
      textdata=>  this.data= <string>textdata, 
// textdata=>  console.log(textdata) -- here I can see the text values.
      error => this.errorMessage = <any>error from server
    );
    //console.log(this.data); --here I dont see any text values
  }

Upvotes: 0

Views: 575

Answers (1)

BeetleJuice
BeetleJuice

Reputation: 40936

In your ngOnInit, when you make the call to getValueFromJenkins, the response doesn't arrive right away. That's why on the next line this.data is still undefined. The response arrives later, inside the .subscribe(). You have at least 3 options:

Use a Data Resolve Guard

Angular allows you to use a special service called a Resolve Guard that will fetch asynchronous data during the route loading process and will initialize your component only after the data has arrived. The benefit of this is that the data will be available synchronously whithin ngOnInit(), so there will be no lag between component load and data load, and no fuss dealing with asynchronous operations. This is probably the best solution but it requires modifying your route configs and writing a new Service class. See the docs

Subscribe in ngOnInit

In ngOnInit, you could wait until the data returns to use it:

this._jenkinsService.getTextFromJenkins(this.serviceName).subscribe(
   result => this.data = result // note this happens later, once server returns data
);

Use async/await to force browser to wait

The async/await keywords allow you to work with asynchronous code as if it were synchronous, by forcing the browser to wait for execution to resolve before moving on to the next line. To implement this approach, first change getValueFromJenkins() and make it return a promise:

getValueFromJenkins(serviceName):Promise {
  // will subscribe and convert the result to a promise that will resolve
  // with the server's response
  return this._jenkinsService.getTextFromJenkins(serviceName).toPromise();
}

Next, mark ngOnInit as async; you can get the data like so:

async ngOnInit() {
  for(let i = 0; i < this.servicesList.length; i++){
    try{
      this.data = await this.getValueFromJenkins(this.servicesList[i]);
      console.log(this.data); // should work!
    }catch(e) { /* Handle error here */}
  }
}

Upvotes: 1

Related Questions