I have a provider that reads the data from a JSON and I want it to send the data to a page.
To ensure that the data is well sent to the UI I have a console.log that is supposed to show the first element of the array, but the issue is that I don't know how to only call this console.log once the information has been retrieved and passed by the provider.
the intention of course is not the console.log, but to do actual things with the data, once it's passed by the provider.
The provider :
getWorldCities(){
if (this.worldCities) {
// already loaded data
return Promise.resolve(this.worldCities);
}
this.http.get('../assets/city.list.json')
.map(res => res.json()).subscribe(data => {
console.log(data.status);
console.log(data);
console.log(data.headers);
this.worldCities = data;
return Promise.resolve(this.worldCities);
},
err => console.log(err),
() => console.log('yay')
)
}
}
And in my page :
ionViewDidLoad() {
console.log('ionViewDidLoad ChooseCity');
console.log(this.worldCities);
this.AppCitiesProvider.getWorldCities().then(data => {
this.worldCities = data;
console.log(this.worldCities[0]);
});
}
the error I get is :
Cannot read property 'then' of undefined
How should I do that properly ?
The "best practice" for retrieving data and performing operations once the data is received is to use the Observable instead of a promise. Something like this:
In the service:
getMovies(): Observable<IMovie[]> {
return this.http.get<IMovie[]>(this.moviesUrl)
.do(data => console.log(JSON.stringify(data)))
.catch(this.handleError);
}
handleError(error) {
// Your error handling here.
console.error(error);
}
In the component:
ngOnInit(): void {
this.movieService.getMovies()
.subscribe(
(movies: IMovie[]) => {
this.movies = movies;
this.filteredMovies = this.performFilter(this.listFilter);
},
(error: any) => this.errorMessage = <any>error);
}
Any code within the first callback function to the subscribe will only be executed after the data is retrieved.
See this for more information (and pictures!) Subscribe to observable is returning undefined
Related
here is my complete code function example:
public scan(formData: Object): Observable<any> {
let url = this.remoteUrl;
let result;
this.onlineService.isOnline$.subscribe( (isOnline) => {
if (isOnline) {
console.log('services is online connected');
result = this
._http
.post(url, formData, { headers: headers })
.pipe(map((res: any) => {
// console.log(res);
let response = res;
return response;
}),
catchError(error => {
if (error.status === 401 || error.status === 403) {
// handle error
}
return throwError(error);
}));
}else{
console.log('services are offline');
result = this.dbService.getByIndex('safety', 'code', formData['trafoStation']).subscribe( (location) => {
return location;
});
}
});
console.log(result);
return result;
};
actually, I need to run two different services based on an internet connection if the connection is available then call server API otherwise store on offline ngx-indexed-db.
i have stored data both online and offline.
getting undefined in result.
Result is undefined because it's an async operation: this.onlineService.isOnline$ has not emmited yet, but you already have return result, thus the undefined.
Also, the way you combine your observables is not right. You should NOT create new observables (and subscribe to them) in a subscribe method. That lead to weird side effects and memory leaks down the line.
Here's my proposal to get your code to work. I used the switchMap operator to return either your apiCall or your store operation based on isOnline$ value. SwitchMap is used to combine a higher observable with an inner observable and flatten the stream. It will also interupt the current subscription each time isOnline$ emits:
private _handleServices(formData, isOnline: boolean): Observable<any> {
console.log(`services are ${isOnline ? 'online': 'offline'}`);
const url = this.remoteUrl;
const apiCall$ = this._http.post(url, formData, { headers: headers })
.pipe(
catchError(error => {
if (error.status === 401 || error.status === 403) {
// handle error
}
return throwError(error);
})
);
const store$ = this.dbService.getByIndex('safety', 'code', formData['trafoStation']);
return (isOnline) ? apiCall$ : store$;
}
public scan(formData: Object): Observable<any> {
return this.onlineService.isOnline$.pipe(
switchMap((isOnline) => this._handleServices(formData, isOnline)),
tap(res => console.log(res))
);
};
Then, when you call your function in your component, you will call it like this:
this.scan(formData).subscribe(res => /* handle scan response */);
I'm trying to build a hackernews clone as a practice to reactjs. Here I'm trying to build this only with react and later I'm going to build this with Redux.
This is my component structure.
--main
|--menubar
|--articles
Here is the codepen for the project.
I'm having two issues here.
1.)
Here I'm passing data through state and props.I'm calling the API in componentDidMount method on menubar component and pass it to the articles component through main component. But it doesn't render the list when it receives the data through props in componentWillReceiveProps method. To render it I have to click News (which has no connection with fetching data, it's just printing a log) which will recall the API method. How can I render the data in this.state.articleList when the data is received through props and set the data.
2.)
In the API call, I have defined to get first 20 posts only. But when I click news, I'm getting random number of (<20) posts rendered in each time. Why is that ? As the API call is same, shouldn't it render same amount of(20) posts ? Why it differ ?
Is the both issues because of Asynchronous methods ? If so how can I solve them ?
Actually its because of async, i edited it using the async library edited the fetchdata() and added getItems().
The advantage of using map is that it will return an array of results itself so we need not maintain an array.
var async = require("async");
fetchdata() {
fetch("https://hacker-news.firebaseio.com/v0/topstories.json")
.then(res => res.json())
.then(data => {
this.setState({ storyList: data }, () => {
this.getItems(this.state.storyList);
});
})
.catch(err => console.log(`Error fetching data : ${err}`));
}
getItems(storyList) {
async.mapLimit(storyList, 10,
function(item, callback) {
console.log(item);
fetch(`https://hacker-news.firebaseio.com/v0/item/${item}.json`)
.then(res => res.json())
.then(data => {
//pass the data here
callback(null, data);
});
},
function(err, dataArray) {
if (err) {
console.error(err.message);
} else {
//dataArray will contain an array of results from map
this.props.getData(dataArray);
}
}
);
}
Hi after getting the data inside getItems binding the data to callback getData as follows
getItems(storyList) {
var story_list = [];
async.mapLimit(
storyList,
10,
((item, callback) =>{
console.log(item);
fetch(`https://hacker-news.firebaseio.com/v0/item/${item}.json`)
.then(res => res.json())
.then(data => {
story_list.push(data);
this.props.getData(story_list);
});
}),
((err) =>{
if (err) {
console.error(err.message);
} else {
this.props.getData(story_list);
}
})
);
}
Hi In my Angular Component, i have this code in one of my methods
this.http.get("http://localhost:8080/poeples")
.map(
resp => { resp = resp.json(); }
).subscribe(
(data) => { this.poeples = data; },
err => console.log(err)
);
In network tab in chrome dev inspector i saw that my get call returning result, but data is undefined.
Why?
The reason it was not working originally, is because you had this:
resp => { resp = resp.json(); }
You are not returning a value. When you use the curly braces, you have to explicitly define a return value. All you had to do was:
resp => { return resp.json(); }
Or remove the braces:
resp => resp.json()
// Your code that isn't working:
/*this.http.get("http://localhost:8080/poeples")
.map(
resp => {
resp = resp.json()
}
).subscribe(
(data) => {
this.poeples = data;
},
err => console.log(err)) ;*/
// Working code:
#import { map } from 'rxjs/operators';
this.http.get('http://localhost:8080/poeples')
.pipe(
// In your example, you are creating a new function with the {} wrapped around this line, but you aren't returning anything, so the return value of the "data" below becomes "undefined."
map((response) => response.json())
)
.subscribe(
(data) => {
this.poeples = data;
},
(err) => console.log(err)
);
Error - when subscribing request success code not working.
This is my code I'm working on
this.userService.addDeliverDetails(form.value)
.subscribe(
res=>{
this.have=true;
},
error=>{
console.log('error')
}
);
Try to console.log() in error and if it logged that's mean your data coming from the server as text not JSON formatted.
Two solutions 1) change angular to accept text responses
2) change server response to json not to string (plain text)
I've written a function to send a http put request to update some data but it says, that it is not recieving any data:
updateHuman(human: Human) {
const url = `${this.url}/${human.id}`;
const data = JSON.stringify(human);
return this.http.put(url, data).map(
response => response.json().data as Human,
error => console.log(error)
);
}
After I've changed my function to the following, it is working:
updateHuman(human: Human) {
const url = `${this.url}/${human.id}`;
const data = JSON.stringify(human);
return this.http.put(url, data).map(() => human);
}
Could someone explain me, why the first function is not working but second is working?
Observables are lazy, you need to be subscribed to them for them to work and retrieve anything. Did you subscribe to your method? Example:
methodToUpdateHuman(human): void{
...
this.updateHuman(human).subscribe((response) => {
//do something with the response
console.log.("Response is: ", response);
},
(error) => {
//catch the error
console.error("An error occurred, ", error);
});
}
I suggest you read through the Angular Tour Of Heroses, it's based in angular 2 and most of the functionality is functional in angular 4, there is a section dedicated to http requests: https://angular.io/tutorial/toh-pt6
In the second example you are not returning the response within the map, you are returning the human that was originally passed in.
So, basically you are creating an illusion that it is working, when it isn't.
Probably best to test your API with something like PostMan, to see if you can get it working with that first.
You use map method incorrectly, read more about this method in documentation: http://xgrommx.github.io/rx-book/content/observable/observable_instance_methods/map.html
If you want receive response from server your code should look like that:
updateHuman(human: Human) {
const url = `${this.url}/${human.id}`;
const data = JSON.stringify(human);
return this.http.put(url, data).subscribe(
response => response.json().data as Human,
error => console.log(error)
);
}
You can use map method if you want to modify server response(map some objects to other structures etc.):
updateHuman(human: Human) {
const url = `${this.url}/${human.id}`;
const data = JSON.stringify(human);
return this.http.put(url, data)
.map(response => { return response.json() }) // you can get json response here
.subscribe(
response => response.data as Human, // -- change here --
error => console.log(error)
);
}
map method returns Observable object, so you can subscribe that and wait for response, error or simple complete method(third parameter of subscribe()):
http://xgrommx.github.io/rx-book/content/observable/observable_instance_methods/subscribe.html
I'm using Observable from rxJS in my Angular2 with TypeScript application. I would like to take a copy of the http get response data.
Service:
getSizes(sku: number): Observable<SizeList[]> {
let api = this.host + this.routes.sizes + sku;
return this._http.get(api)
.map((response: Response) => <SizeList[]>response.json())
.catch(this.handleError);
}
Component:
getSizes() {
this._productService.getSizes(this.productColour)
.subscribe(
sizes => this.sizes = sizes,
error => this.errorMessage = <any>error);
}
How can I take a copy of this.sizes? If I try to take a copy at the end of my components getSizes(), it's undefined.
I think that your problem is related to the asynchronous aspect of observables. At the end of the getSizes method, the data are there yet. They will be available within the subscribe callback:
getSizes() {
this._productService.getSizes(this.productColour)
.subscribe(
sizes => {
this.sizes = sizes;
console.log(this.sizes); // <------
},
error => this.errorMessage = <any>error);
}
If you want to return the value from the getSizes method, you need to return an observable and let the method caller subscribe on it:
getSizes() {
return this._productService.getSizes(this.productColour)
.catch(error => this.errorMessage = <any>error);
}
someOtherMethod() {
this.getSizes().subscribe(sizes => this.sizes = sizes);
}
This is because HTTP requests are made asynchronously in JS/Angular 2, so logic at the end of your getSizes() method is probably running before the method this._productService.getSizes(...) has finished loading your content.
You should place your logic therefore in the subscribe() method like this:
getSizes() {
this._productService.getSizes(this.productColour)
.subscribe(
sizes => {
this.sizes = sizes
// more logic here
},
error => this.errorMessage = <any>error);
// code here gets executed before the subscribe() method gets called
}