Angular 5 is not displaying response value from server - javascript

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

Related

How to get body from HttpErrorResponse in Angular 6?

I have created a REST API call in my Angular app which downloads a file.
I am setting responseType to 'blob' since I am expecting a file in response.
But when there is no file available at the server the Response has a error code as 404 i.e Bad Request with some message in body.
But I am not able to parse that error message from body since HttpErrorResponse is giving a blob object in error.error
How do I get the actual body from the error object instead of blob.
Also is there any way to configure angular that on success of an api call parse the request in blob otherwise parse it in json ???
Hoping for a resolution
Try this
if(error.error instanceof Blob) {
error.error.text().then(text => {
let error_msg = (JSON.parse(text).message);
console.log(error_msg)
});
} else {
//handle regular json error - useful if you are offline
}
Parameter: { observe: 'response' }, let you read the full response including the headers. See the below description:-
Tell HttpClient that you want the full response with the observe option:
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(this.configUrl, { observe: 'response' });
}
Now HttpClient.get() returns an Observable of typed HttpResponse rather than just the JSON data.
this.configService.getConfigResponse()
// resp is of type `HttpResponse<Config>`
.subscribe(resp => {
// display its headers
const keys = resp.headers.keys();
this.headers = keys.map(key =>
`${key}: ${resp.headers.get(key)}`);
// access the body directly, which is typed as `Config`.
this.config = { ...resp.body };
});
and getting Error body like that:-
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 throwError(
'Something bad happened; please try again later.');
};
import { catchError} from 'rxjs/operators';
getConfig() {
return this.http.get<Config>(this.configUrl)
.pipe(
catchError(this.handleError)
);
}
Reference: https://angular.io/guide/http : Reading the full response
Change your code accordingly.
For future visitors (since the title is generic):
If the backend returns JSON upon error (ideally, following RFC 7807, which would also mean application/problem+json content-type too), the error.error is a JSON object, not a string. So to print it, for example, you would need to stringify it first:
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${JSON.stringify(error.error)}`);
I believe the confusion starts from the official Angular documentation, which contains this statement:
// 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}`);
But with error.error being a JSON object (in the standard cases), you get printed [object Object] for the body instead of the string representation of that JSON object. Same unhelpful output if you try ${error.error.toString()}.
If the returned ContentType are different then you can leverage it to distinguish whether it's a correct binary file or a text in binary format.
lets consider you have two files, a service, which handles your request and a component which does the business logic
Inside your service, have your download method like:
public downloadFile(yourParams): Observable<yourType | Blob> {
return this._http.post(yourRequestURL, yourParams.body, {responseType: 'blob'}).pipe(
switchMap((data: Blob) => {
if (data.type == <ResponseType> 'application/octet-stream') {
// this is a correct binary data, great return as it is
return of(data);
} else {
// this is some error message, returned as a blob
let reader = new FileReader();
reader.readAsBinaryString(data); // read that message
return fromEvent(reader, 'loadend').pipe(
map(() => {
return JSON.parse(reader.result); // parse it as it's a text.
// considering you are handling JSON data in your app, if not then return as it is
})
);
}
})
);
}
In your component
public downloadFile(params): void {
this._service.downloadFile(params)
subscribe((data: yourType | Blob) => {
if (data instanceof Blob) {
fileSaverSave(data, filename); // use fileSaver or however you are downloading the content
// add an import for saveAs (import { saveAs as fileSaverSave } from 'file-saver';)
} else {
// do your componnet logic to show the errors
}
})
}
If you wish, you can have everything inside your component itself.
You could try a separate error handler function, which returns the response as T as follows -
public handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
Then simply use it to track errors in your request as follows -
return this.http.post(this.appconstants.downloadUrl, data, { responseType: 'blob' }).pipe(
map(this.loggerService.extractFiles),
catchError(this.loggerService.handleError<any>('downloadFile')) // <----
);
FYI, the function extractFiles that I used above to return a file is as follows -
public extractFiles(res: Blob): Blob{
return res;
}

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

Subscribe to observable is returning undefined

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.

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

No XHR request is fired when calling http function

I am struggling to set up a service in angular2 that should communicate with a REST backend.
I was trying to set up a minimal example that should sent requests to a rest backend and process the response. The Service is called correctly and my console shows the message
'calling the star wars api';
The problem is that I was checking the xhr tab in the network section of the browser dev tools and no xhr requests are fired.
It seems like the functions extractData and handleError do not seem to be called either although I defined them in map and catch.
Am I missing something here? Why is there no xhr request sent when I call this service?
This is my Service:
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { Token } from './token';
#Injectable()
export class UserService {
constructor(private http: Http) {
}
getAll() : Observable<Token[]>{
console.log('calling the star wars api');
let people$ = this.http
.get('http://swapi.co/api/people', {headers: this.getHeaders()})
.map(this.extractData)
.catch(this.handleError);
return people$;
}
private getHeaders(){
let headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
private extractData(res: Response) {
console.log('data extracted!');
let body = res.json();
return body.data || { };
}
private handleError (error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
console.log('an error ocurred');
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
I set up the project, component and service with angular-cli. I am using angular2 version 2.4.0
thanks to 0x2D9A3's comments I was able to resolve the issue.
The problem was the way that I called the UserService from my component.
this.userService.getAll()
just called the service but did not fire any XHR request.
Howewer,
this.userService.getAll().subscribe((result) => {
if (result) {
console.log('successfull xhr request');
}
}
Did call the xhr request successfully because this is the way how an Observable has to be consumed.
This is from the angular docs
Think of an Observable as a stream of events published by some source.
To listen for events in this stream, subscribe to the Observable.
These subscriptions specify the actions to take when the web request
produces a success event (with the data in the event payload) or
a fail event (with the error in the payload).

Categories