Subscribe to observable is returning undefined - javascript

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

Maybe some pictures help?
The numbers here indicate the order of operations.
Send the Http Request
Component is initialized and calls the getMovies method of the movieService.
The movieService getMovies method returns an Observable. NOT the data at this point.
The component calls subscribe on the returned Observable.
The get request is submitted to the server for processing.
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
At some LATER point in time ...
The movies are returned to the service.
If the process was successful, the first callback function is executed.
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
To fix it:

objResponse;
this.service.getData().subscribe((result: any)=> {
this.objResponse=result;
}
Returning something won't required

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

Try with:
getData(): any {
return this._http.get('api.json');
}
or
getData(): any {
return this._http.get('api.json').map((response: Response) => {
response.json();
})

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

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

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.

Related

TypeScript : Value assigned inside HTTPClient scope is not accessible from outside (within the same function)

On Load, I'm calling the function getData() which will read & map the response to Data[] model and returns back the result. But the function returning undefined object though it's value is assigned.
import { HttpClient} from '#angular/common/http';
import {Data} from "../model";
export class Service {
constructor(private http: HttpClient) {}
getData():Data[]{
var data: Data[];
this.http.get(url,{ responseType: 'blob'}).subscribe(response => {
response.text().then(value => {
data = <Data[]>JSON.parse(value);
console.log(data); ----> {Printing the values as expected i.e., Data Array}
});
});
console.log(data); ----> {Printing Undefined}
return testData;
}
onLoad(){
var data:Data[] = this.getData();
console.log(data) ---------> {Printing Undefined}
}
}
Data Class :
export class Data{
id:number;
name:string;
value:string;
}
Http calls are async calls. Subsequent call will get executed and will not wait for the promise to be resolve.
Your dependent code should be written once it resolved.
I'll just show what I would like to do, here's a little bit reconstruction for your codes:
import {from, Observable} from 'rxjs'; // <= several imports you might care
import {map, switchMap} from 'rxjs/operators'; // <= several imports you might care
export class Service {
constructor(private http: HttpClient) {}
getData(): Observable<Data[]> {
return this.http.get(someurl, {responseType: 'blob'})
.pipe(
switchMap((blob: Blob) => {
return from(blob.text());
}),
map(text => {
return JSON.parse(text) as Data[];
})
);
}
onLoad(): void {
this.getData().subscribe((data: Data[]) => {
console.log(data); // <=== this is where you can log data
});
}
}
The basic idea is you should return a aync(aka Observable) result for your http request, and pipe the first result(type of Blob) to another Observable(which is built from a promise) of string value, then map it to a Data array at last, in onLoad you should subscribe the async result and check the data in subscription. In a word, you should keep the data flow asynchronously until it is been resolved

Angular Positing Console Errors on API Call

I've got an angular site that's reporting error messages to the console, but it's working on screen. I suspect it's due to how the page renders, but after googling the error and Angular rendering I can't see how to fix it.
This is how the console looks:
This is the service that's handling the API calls:
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from "#angular/http";
#Injectable()
export class WmApiService {
private _baseUrl = "http://localhost:58061/";
tempuser = "WebDevelopWolf";
modules: any;
constructor(private _http: Http) {
console.log('Wavemaker API Initialized...');
}
// On successful API call
private extractData(res: Response) {
let body = res.json();
return body || {};
}
// On Error in API Call
private handleError(error: any): Promise<any> {
console.error('An error occurred', error);
return Promise.reject(error.message || error);
}
// Basic Get W/ No Body
getService(url: string): Promise<any> {
return this._http
.get(this._baseUrl + url)
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
// Basic Post W/ Body
postService(url: string, body: any): Promise<any> {
console.log(body);
let headers = new Headers({'Content-Type': 'application/json'});
return this._http
.post(this._baseUrl + url, body, {headers: headers})
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
}
And finally the call to the service:
ngOnInit() {
this.getUserProfile();
}
// Fill the user profile information
getUserProfile() {
this._wmapi
.getService("User/" + this._wmapi.tempuser)
.then((result) => {
// Push the user to UI
this.userProfile = result;
// Set the user avatar
this.userAvatar = "../assets/users/profile/" + this.userProfile.Username + ".png";
})
.catch(error => console.log(error));
}
I've had a couple of people tell me in the past that I shouldn't be using promises because they're outdated, but it's just what familiar with from working with Ionic a couple of years back - however, if there is a better way to do it I'm definitely open to suggestion, especially if it's the promise that's causing the issue.
try:
<div>{{some-value?.UserFullName}}</div>
Your some-value object doesn't have the value until the API response arrives. Then use ? to apply the null check until the response arrives.
The problem is that angular is trying to render your component and this.userProfile is not instantiated yet by that moment, so you are trying to resolve the props of undefined.
You need to handle the case when there is no userProfile, so you can either use ngIf for that section of template, or use getter to get those props, or perform check directly in template {{userProfile && userProfile.someProp}}

What is the correct approach to have Promise.all?

I have two calls that will be based on request it could be single or both api calls at the same time, so i have implemented below code not sure if thats the correct approach to use promise.all , Also want to throw error back to user in case any promise failed.
execute function is executed from the routes.
if there is better way to implement i would appreciate the feedback.
main.ts
public async execute(#Request() request: express.Request): Promise<[any] | any> {
if (request.body.lob === "credit") {
return this.getCardDetails(request);
}
if (request.body.lob === "individual") {
return this.getAccountDetails(request);
}
return Promise.all([this.getCardDetails(request), this.getAccountDetails(request)]);
}
#Post('getAccountDetails')
private async getAccountDetails(#Body() request: any): Promise<any> {
// process retrieveData Call and get response
}
#Post('getCardDetails')
private async getCardDetails(#Body() request: any): Promise<any> {
// process cardDetails Call and get response
}

Angular 5 is not displaying response value from server

It seems my client is not capturing the response value from the server and displaying it.
Here is my component code:
export class MyComponent implements OnInit {
data: string;
constructor(private myService: MyService) {}
ngOnInit() {}
testCall() {
this.myService.getData().subscribe(data => this.data = data);
console.log("Data: " + this.data);
}
}
The service code:
#Injectable()
export class MyService {
private url = 'http://localhost:5000/myproj/api/test';
constructor(private http: HttpClient) { }
// Get data from the server
getData(): Observable<string> {
console.log("in getData() method");
return this.http.get<string>(this.url)
.pipe(
catchError(this.handleError) // then handle the error
);
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return new ErrorObservable('Something went wrong; please try again later.');
};
}
The request goes to the server, and the server responds with the data in the response body, and a status code of 200, which you can see in developer tools in Internet Explorer:
But for some reason, when I call the service method getData(), the angular client code calls the catchError() method I defined, and prints:
Backend returned code 200, body was: [object Object]
ERROR Something went wrong; please try again later.
Why is the server returning status 200 (OK), but the Angular client is calling the catchError() method?
EDIT:
Here is my server side API code:
#RequestMapping(value = "/test", method = RequestMethod.GET, produces = "text/plain")
public String testApi(HttpServletRequest request) {
System.out.println("in /test");
String response = "my response";
return response;
}
The Response Body is not proper JSON format, hence the "Invalid character" error which is produced from the deserialization. The service is expecting properly formed JSON.
Update your API to return a valid JSON object by using "application/json" and returning an object as shown in the following post: Spring MVC - How to return simple String as JSON in Rest Controller
You need to place the console.log inside the .subscribe() method
this.myService.getData().subscribe(data => {
this.data = data;
console.log(this.data);
});
You have to set responseType to 'text' in a request options object. Here's a sample:
return this.http.get(`myApi/ExampleMethod/param`, { responseType: 'text' })
.pipe(
catchError(
this.errorHandler.handleError.bind(this)
)
);

Angular2 - Pass http.get response to class object

I am new to angular. I have a json file where I can configure the url that I need to use in my app.
app/config/development.json
{
"apiUrl": "http://staging.domain.com:9000/",
"debugging": true
}
And below is my code in config.service.ts:
export class ConfigService {
private apiURL:any;
constructor (private http: Http) {}
getApiURL(){
this.http.get("app/config/development.json").map(res:Response=>res.json())
.subscribe(data=>{
this.apiURL = data;
})
console.log(this.apiURL);//this returns undefined
}
}
I want to make this.apiURL to contain the response of the http.get.
And when I create another method, the value of this.apiURL is still the same from the method getAPIURL().
someMethod()
{
console.log(this.apiURL)//this must contain the response from http.get
}
You can do something like this.
In your service file.
//whatever model u defined
getApiURL():Observable<Object[]>{
return this.http.get(this.whateverURL)
.map(res:Response=>res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
In your component file
yourData:Object[];
//whatever Model u defined.
//assuming yourService is your service instance which u did in constructor.
this.yourService.getApiURL()
.subscribe(
yourData=>{
this.yourData=yourData;
},err=>{
console.log(err);
alert("Something went wrong");
}
)
}

Categories