I am looking for data in an API via Get request, I need the data inside my OnInit to use in composing other data. The problem is that the method is being called but it is as an async method (without await), it passes everywhere but when the return is obtained the excution of the main method has already been finished with no results. I tried the implementation of asynchronous methods but it did not solve.
service:
getObjects(): MyClass[] {
let objects = new Array<MyClass>();
this.obterTodos<MyClass>(this.uriApi)
.map(res => res.map(x => new MyClass(x.Description, x.Id)))
.subscribe(entities => {
objects = entities;
});
return objects ;
}
get request
public getObjects<TEntity>(url: string): Observable<TEntity[]> {
return this.httpService
.get(this.serviceUrl + url, this.options)
.map(res => res.json())
.catch(this.handleError);
}
component:
ngOnInit() {
this.myObjects= this.setorService.obterSetores();
console.log('this.myObjects is empty here');
}
so you'll want to subscribe to your observable in the component. This is typically done so the component can determine when the http request should be ran & so the component can wait for the http request to finish (and follow with some logic).
// subscribe to observable somewhere in your component (like ngOnInit)
this.setorService.obterSetores().subscribe(
res => {
this.myObjects = res;
},
err => {}
)
now the component knows when the http request finishes
Related
I'm new to web development and as part of a project have made a Django React Redux app. The frontentd has a component with form entry - when a form is submitted, the component calls an action that sends an axios request to a python twitter crawler script in the backend. The script returns a response containing a string array (the tweets are inside the array). I then try to dispatch the array contained in the response.data, as a payload to a reducer. However I keep getting an error saying that the payload of what I have dispatched is undefined.
This is the return statement of the python method that is called - corpus is a string array.
return JsonResponse({"results:": corpus})
This is the code for the action that sends a GET request to the python method. It's then returned the string array inside a json object.
// GET_TWEETS - get list of tweets from hashtag search
export const getTweets = (hashtag) => dispatch => {
axios
.get('http://localhost:8000/twitter_search', {
params: {text: hashtag}
})
.then(res => {
console.log(res.data); //check if python method returns corpus of tweets
//const results = Array.from(res.data.results);
dispatch({
type: GET_TWEET,
payload: res.data.results
});
})
.catch(err => console.log(err));
}
The console log shows that the object is returned successfully from the python script.
console log
This is the code for my reducer. I want my action to contain the string array and then assign that string array to 'tweets' in the state, so that I can return this to a component and then access the array from the component and then display contents of thisx array of tweets on the frontend
import { GET_TWEET } from '../actions/types';
const initialState = {
tweets: []
}
export default function(state = initialState, action) {
console.log(action.payload)
switch(action.type) {
case GET_TWEET:
return {
...state,
tweets: [...action.payload]
}
default:
return state;
}
}
Also, this is some of the code for the component that I want to receive the string array, I hope I have set this part up properly:
export class Tweets extends Component {
static propTypes = {
tweets: PropTypes.array.isRequired,
getTweets: PropTypes.func.isRequired
}
...
const mapStateToProps = state => ({
tweets: state.TweetsReducer.tweets
});
export default connect(mapStateToProps, { getTweets })(Tweets);
Here is the error I get from the console. Console logging the payload of the action also shows that its value is undefined: undefined error
I've been trying to solve this for several days now, but am completely lost and I have a hunch the solution to this is pretty simple...
I have service which returns an observable which does an http request to my server and gets the data. I want to use this data but I always end up getting undefined. What's the problem?
Service:
#Injectable()
export class EventService {
constructor(private http: Http) { }
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
}
Component:
#Component({...})
export class EventComponent {
myEvents: any;
constructor( private es: EventService ) { }
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
});
console.log(this.myEvents); //This prints undefined!
}
}
I checked How do I return the response from an asynchronous call? post but couldn't find a solution
Reason:
The reason that it's undefined is that you are making an asynchronous operation. Meaning it'll take some time to complete the getEventList method (depending mostly on your network speed).
So lets look at the http call.
this.es.getEventList()
After you actually make ("fire") your http request with subscribe you will be waiting for the response. While waiting, javascript will execute the lines below this code and if it encounters synchronous assignments/operations it'll execute them immediately.
So after subscribing to the getEventList() and waiting for the response,
console.log(this.myEvents);
line will be executed immediately. And the value of it is undefined before the response arrives from the server (or to whatever that you have initialized it in the first place).
It is similar to doing:
ngOnInit(){
setTimeout(()=>{
this.myEvents = response;
}, 5000);
console.log(this.myEvents); //This prints undefined!
}
**Solution:**
>So how do we overcome this problem? We will use the callback function which is the `subscribe` method. Because when the data arrives from the server it'll be inside the `subscribe` with the response.
So changing the code to:
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-- not undefined anymore
});
will print the response.. after some time.
**What you should do:**
There might be lots of things to do with your response other than just logging it; you should do all these operations inside the callback (inside the subscribe function), when the data arrives.
Another thing to mention is that if you come from a Promise background, the then callback corresponds to subscribe with observables.
**What you shouldn't do:**
You shouldn't try to change an async operation to a sync operation (not that you can). One of the reasons that we have async operations is to not make the user wait for an operation to complete while they can do other things in that time period. Suppose that one of your async operations takes 3 minutes to complete, if we didn't have the async operations then the interface would freeze for 3 minutes.
Suggested Reading:
The original credit to this answer goes to: How do I return the response from an asynchronous call?
But with the angular2 release we were introduced to typescript and observables so this answer hopefully covers the basics of handling an asynchronous request with observables.
Making a http call in angular/javascript is asynchronous operation.
So when you make http call it will assign new thread to finish this call and start execution next line with another thread.
That is why you are getting undefined value.
so make below change to resolve this
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-this become synchronous now
});
You can use asyncPipe if you use myEvents only in template.
Here example with asyncPipe and Angular4 HttpClient example
Observables are lazy so you have to subscribe to get the value. You subscribed it properly in your code but simultaneously logged the output outside the 'subscribe' block. That's why it is 'undefined'.
ngOnInit() {
this.es.getEventList()
.subscribe((response) => {
this.myEvents = response;
});
console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}
So if you log it inside the subscribe block then it will log response properly.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //Inside the subscribe block 'http response'
});
}
Here the problem is, you are initializing this.myEvents into subscribe() which is an asynchronous block while you are doing console.log() just out of subscribe() block.
So console.log() getting called before this.myEvents gets initialized.
Please move your console.log() code as well inside subscribe() and you are done.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents);
});
}
The result is undefined because angular process async .
you can trying as below:
async ngOnInit(){
const res = await this.es.getEventList();
console.log(JSON.stringify(res));
}
Also make sure that you map your response to a json output. Otherwise it will return plain text. You do it this like this:
getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=>{ return res.json();}) <!-- add call to json here
.catch((err)=>{return err;})
}
Undefined because the value here is logged before any data from the service is set from that above subscribe service call. So you have to wait until the ajax call finishes and set the data from the response data.
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
Here make the Console log inside the subscribe method that will make the log when the data is set in myEvents variable.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
// This prints the value from the response
console.log(this.myEvents)
});
}
To do this you have 2 options:
Suppose we have a service which is returning shipping details array :
getShippingPrices(): Observable<IShippingDetails[]> {
return this.http.get<IShippingDetails[]>('/assets/shipping.json');
}
1. Use Async pipe : Easy way when you just want to show the result in template
In the component class directly assign the observable to variable:
export class ShippingComponent implements OnInit {
shipOptions1 = this.cartService.getShippingPrices();
constructor(private cartService: CartService) {}
ngOnInit() {}
}
and then use async pipe in template :
<div *ngFor="let s of shipOptions1 |async">
<label>{{s.type}}</label>
</div>
Refer: Check the 4th point in this URL
https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice
2. Use Subscribe : When you want to manipulate it or want do some business logic on/from response
export class ShippingComponent implements OnInit {
shipOptions2: IShippingDetails[] = [];
constructor(private cartService: CartService) {}
ngOnInit() {
this.cartService.getShippingPrices().subscribe(response => {
this.shipOptions2 = response;
//console.log(this.myEvents);
//All other code using shipOptions2
});
}
}
You can simply try this method-
let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http
.get(this.yourSearchUrlHere, options) // the URL which you have defined
.map((res) => {
res.json(); // using return res.json() will throw error
}
.catch(err) => {
console.error('error');
}
I have service which returns an observable which does an http request to my server and gets the data. I want to use this data but I always end up getting undefined. What's the problem?
Service:
#Injectable()
export class EventService {
constructor(private http: Http) { }
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
}
Component:
#Component({...})
export class EventComponent {
myEvents: any;
constructor( private es: EventService ) { }
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
});
console.log(this.myEvents); //This prints undefined!
}
}
I checked How do I return the response from an asynchronous call? post but couldn't find a solution
Reason:
The reason that it's undefined is that you are making an asynchronous operation. Meaning it'll take some time to complete the getEventList method (depending mostly on your network speed).
So lets look at the http call.
this.es.getEventList()
After you actually make ("fire") your http request with subscribe you will be waiting for the response. While waiting, javascript will execute the lines below this code and if it encounters synchronous assignments/operations it'll execute them immediately.
So after subscribing to the getEventList() and waiting for the response,
console.log(this.myEvents);
line will be executed immediately. And the value of it is undefined before the response arrives from the server (or to whatever that you have initialized it in the first place).
It is similar to doing:
ngOnInit(){
setTimeout(()=>{
this.myEvents = response;
}, 5000);
console.log(this.myEvents); //This prints undefined!
}
**Solution:**
>So how do we overcome this problem? We will use the callback function which is the `subscribe` method. Because when the data arrives from the server it'll be inside the `subscribe` with the response.
So changing the code to:
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-- not undefined anymore
});
will print the response.. after some time.
**What you should do:**
There might be lots of things to do with your response other than just logging it; you should do all these operations inside the callback (inside the subscribe function), when the data arrives.
Another thing to mention is that if you come from a Promise background, the then callback corresponds to subscribe with observables.
**What you shouldn't do:**
You shouldn't try to change an async operation to a sync operation (not that you can). One of the reasons that we have async operations is to not make the user wait for an operation to complete while they can do other things in that time period. Suppose that one of your async operations takes 3 minutes to complete, if we didn't have the async operations then the interface would freeze for 3 minutes.
Suggested Reading:
The original credit to this answer goes to: How do I return the response from an asynchronous call?
But with the angular2 release we were introduced to typescript and observables so this answer hopefully covers the basics of handling an asynchronous request with observables.
Making a http call in angular/javascript is asynchronous operation.
So when you make http call it will assign new thread to finish this call and start execution next line with another thread.
That is why you are getting undefined value.
so make below change to resolve this
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //<-this become synchronous now
});
You can use asyncPipe if you use myEvents only in template.
Here example with asyncPipe and Angular4 HttpClient example
Observables are lazy so you have to subscribe to get the value. You subscribed it properly in your code but simultaneously logged the output outside the 'subscribe' block. That's why it is 'undefined'.
ngOnInit() {
this.es.getEventList()
.subscribe((response) => {
this.myEvents = response;
});
console.log(this.myEvents); //Outside the subscribe block 'Undefined'
}
So if you log it inside the subscribe block then it will log response properly.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents); //Inside the subscribe block 'http response'
});
}
Here the problem is, you are initializing this.myEvents into subscribe() which is an asynchronous block while you are doing console.log() just out of subscribe() block.
So console.log() getting called before this.myEvents gets initialized.
Please move your console.log() code as well inside subscribe() and you are done.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
console.log(this.myEvents);
});
}
The result is undefined because angular process async .
you can trying as below:
async ngOnInit(){
const res = await this.es.getEventList();
console.log(JSON.stringify(res));
}
Also make sure that you map your response to a json output. Otherwise it will return plain text. You do it this like this:
getEventList(): Observable<any> {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=>{ return res.json();}) <!-- add call to json here
.catch((err)=>{return err;})
}
Undefined because the value here is logged before any data from the service is set from that above subscribe service call. So you have to wait until the ajax call finishes and set the data from the response data.
getEventList(): Observable<any>{
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get("http://localhost:9999/events/get", options)
.map((res)=> res.json())
.catch((err)=> err)
}
Here make the Console log inside the subscribe method that will make the log when the data is set in myEvents variable.
ngOnInit(){
this.es.getEventList()
.subscribe((response)=>{
this.myEvents = response;
// This prints the value from the response
console.log(this.myEvents)
});
}
To do this you have 2 options:
Suppose we have a service which is returning shipping details array :
getShippingPrices(): Observable<IShippingDetails[]> {
return this.http.get<IShippingDetails[]>('/assets/shipping.json');
}
1. Use Async pipe : Easy way when you just want to show the result in template
In the component class directly assign the observable to variable:
export class ShippingComponent implements OnInit {
shipOptions1 = this.cartService.getShippingPrices();
constructor(private cartService: CartService) {}
ngOnInit() {}
}
and then use async pipe in template :
<div *ngFor="let s of shipOptions1 |async">
<label>{{s.type}}</label>
</div>
Refer: Check the 4th point in this URL
https://angular.io/start/start-data#configuring-the-shippingcomponent-to-use-cartservice
2. Use Subscribe : When you want to manipulate it or want do some business logic on/from response
export class ShippingComponent implements OnInit {
shipOptions2: IShippingDetails[] = [];
constructor(private cartService: CartService) {}
ngOnInit() {
this.cartService.getShippingPrices().subscribe(response => {
this.shipOptions2 = response;
//console.log(this.myEvents);
//All other code using shipOptions2
});
}
}
You can simply try this method-
let headers = new Headers({'Accept': 'application/json'});
let options = new RequestOptions({headers: headers});
return this.http
.get(this.yourSearchUrlHere, options) // the URL which you have defined
.map((res) => {
res.json(); // using return res.json() will throw error
}
.catch(err) => {
console.error('error');
}
I'm doing the nativescript/angular tutorial and I found something in the code that I don't understand and want some clarification.
In chapter 4 (Nativescript modules) when they do a http.get resquest to retrieve the Grocery List and they get the Observable I notice that it is passed throught some maps operator, here is the code:
import { Injectable } from "#angular/core";
import { Http, Headers } from "#angular/http";
import { Observable } from "rxjs/Rx";
import "rxjs/add/operator/map";
import { Config } from "../config";
import { Grocery } from "./grocery";
#Injectable()
export class GroceryListService {
constructor(private http: Http) {}
load() {
let headers = new Headers();
headers.append("Authorization", "Bearer " + Config.token);
return this.http.get(Config.apiUrl + "Groceries", {
headers: headers
})
.map(res => res.json())
.map(data => {
let groceryList = [];
data.Result.forEach((grocery) => { //<------HERE
groceryList.push(new Grocery(grocery.Id, grocery.Name));
});
return groceryList;
})
.catch(this.handleErrors);
}
handleErrors(error: Response) {
console.log(JSON.stringify(error.json()));
return Observable.throw(error);
}
}
My question is, what does "Result" means in the second .map
Why they don't simply put
data.forEach((grocery) => {
I ask because I'm not sure if it is an object property of the resulting observable from .map(res => res.json) or something else.
Could you point me to some documentation of where does that "Result" come from and what it means?
First of all, this line .map(res => res.json()) parses the response body into a json object. Then the second map allows access to this json object under data argument. The json object represented by data is actually a wrapper around the actual response result data using the Result as the key mapped to the data returned from the backend which follows this security advise HERE. So data.Result is just a key mapped to the actual data returned from the backend. The backend could have used a different name for the key, e.g. secret, then you would do data.secret to obtain the data returned from the server
I'm new to REST services, I have an Angular2 client calling a RestEasy JAX-RS service. All I am trying to get is a "Hello World" message in JSON format. I was expecting only a JSON object, but I get my response with the following structure:
_body: "{"message":"Hello World!!"}"
headers: t
ok: true
status: 200
statusText: "OK"
type: 2
url: "http://localhost:8080/helloapp/rest/hello/world"
__proto__: ...
My question is, Is that the way it should be?
I mean, I thought I would be able to access the JSON object straight from the response. Something like
this.service.getHello()
.then( result => {
console.log(JSON.parse(result)); //{message: "Hello World"}
this.message = JSON.parse(result).message;
});
But I actually have to get it from _body:
this.service.getHello()
.then( result => {
this.message = JSON.parse(result._body).message;
console.log(this.message);//Hello World
});
Is it a RestEasy configuration thing, is there a way to change that?
Or
Should I consider that I will always have a field _body in my response with my data, and that's the default response structure?
For eventual consideration, here is my backend code:
HelloWorld Service:
#Path("/hello")
#Produces({ "application/json" })
#Consumes({ "application/json" })
public class HelloWorld {
public HelloWorld() {}
#GET
#Path("/world")
public Message getHello(){
return new Message("Hello World!!");
}
}
My RestEasy version is 3.1.1.Final running in Wildfly 10.1.0.Final
What you're getting back is the Response object from the Http request. This is what all Http operations will return. The easiest way to parse the JSON from that is to just call the json() method on it
this.service.getHello()
.then((res: Response) => {
let obj = res.json();
});
If you want the getHello to just return the object without having to parse it (on the calling client), then you can do it inside the getHello method by mapping it (using the Observable.map operation)
getHello() {
this.http.get(..)
.map((res: Response) => res.json())
.toPromise();
}
As peeskillet says above, you're getting back the entire Response from the request, and while sometimes you may want to examine the headers, perhaps to handle the different return conditions (retry or redirect on 4xx or 5xx responses for example), most of the time we assume a successful request and we just want the payload.
Angular2 encourages the use of Observables, so your service might look something like this:
getHello()
{
return this.http.get(http://localhost:8080/helloapp/rest/hello/world)
}
And your component may look something like this:
data: string;
ngOnInit() {
this.service
.getHello()
.map(response => response.json())
.subscribe (
data => {
this.data = data,
},
err => console.log('Error',err),
() => console.log('data',this.data)
);
}
You call the service, which is an http.get() and returns an Observable object, and we use .map to parse the response as JSON, which also returns an Observable, which we subscribe to.
Subscribe has three callback functions,
.subscribe(success, failure, complete)
In the example above on success we assign the payload - data - to this.data, if the subscribe fails, you log the error, and when it completes, we can do whatever we like, but in this case, we log this.data to the console - that's optional, but I log out the results while developing and then strip them out later.