Logging Data after Subscribe in Angular Observables - javascript

I'm new to the Observables and working my way through the Angular documentation.
Here's how I subscribe to the service and get data.
this.dataService.getData().subscribe(data => this.localData = data)
Now, I'm struggling to log it within the component (console.log) for debug purposes.
I've tried:
this.dataService.getData().subscribe(data => this.localData = data, complete => console.log(this.localData));

You can call console.log inside of the "success" callback:
this.dataService.getData().subscribe(data => {
this.localData = data;
console.log(this.localData);
});
If you want to do it in the "complete" callback (assuming that the Observable does complete):
this.dataService.getData().subscribe(
data => { this.localData = data; },
error => {},
() => { console.log(this.localData); });

this.dataService.getData().subscribe(
(data) => {
this.localData = data,
console.log(this.localData)
}
, error => // the second one is error!),
, complete => //complete is the third one;
You need to console your data inside subscribe on success or in complete. You are printing inside when the subscription returns a error

Related

fetching only in the second click

i have a problem on my fetching.
i fetched API to my project, when i trying to use display on value, it doesn't work on the first click. at the second click the function will run good and everything works.
when im trying to log in the fetching function everything works good, but, at the display function i get an error:
also if i write a number of pokemon to search and click to search it doesnt work but, if i change it and click again, i will get the first pokemon value.
Uncaught TypeError: Cannot read properties of undefined (reading 'name')
im adding the function of the fetch and also the display function
i can also send the git reposetory if anyone want to help.
thenks
let fetetchPokemon = function (inputNum) {
fetch(`${pokemonApi}` + `${inputNum}`)
.then((response) => response.json())
// .then((response) => console.log(response))
.then(
(response) =>
(pokemon = new Pokemon(
response.name,
response.sprites,
response.moves,
response.types,
response.abilities
))
)
// .then((pokemon) => console.log(pokemon.name));
// .then((pokemon) => console.log(pokemon.name))
.then(displayPokemon());
};
let displayPokemon = function () {
pokemonName.innerText = pokemon.name;
console.log(pokemon.name);
pokemonImg.src = pokemon.image.front_default;
displayMoves(pokemon.moves);
displayType(pokemon.types);
displayAbilities(pokemon.abilities);
};
there is also a bin to see the code:
https://jsbin.com/puvotid/edit?html,css,js,output
The display method must be placed same scope with variable assignment.
So the method will look like
const fetetchPokemon = function (inputNum) {
fetch(`${pokemonApi}` + `${inputNum}`)
.then((response) => response.json())
.then(
(response) => {
pokemon = new Pokemon(
response.name,
response.sprites,
response.moves,
response.types,
response.abilities
);
displayPokemon();
}
);
};
As answer below from #Mamoud, you should use async/await or callback invidually, not both
use async and await to wait the results.
const fetchPokemon = async function(){
await fetch(...).then(...)
}

Angular make subscribe to wait for response

I'm trying to subscribe to an Observable and assign some data from the response, but somehow my code it's not waiting for the response. Basically the console.log(this.newIds) is runned first and is always empty because the subscribe doesn't wait for response to come from the backend. How I can force my code to wait for the response to come?
this.repository.getById(Ids).subscribe((response) => {
console.log(response);
this.newIds = response.map((id) => {
return id;
});
});
console.log(this.newIds);
If you put the code in the subscribe callback. It will execute after your receive a response from the back-end. All code you write outside this function is directly execute.
this.repository.getById(Ids).subscribe((response) => {
//Code will execute when back-end will respond
console.log(response);
this.newIds = response.map((id) => {
return id;
});
console.log(this.newIds);
});
//Code will execute immediately
See also : https://angular.io/guide/observables#creating-observables
This is the normal behaviour because your console.log(this.newIds); is outside of the subscription, you just need to move it inside the .subscribe() method:
this.repository.getById(Ids).subscribe((response) => {
console.log(response);
this.newIds = response.map((id) => {
return id;
});
console.log(this.newIds);
});
if you want to use this.newIds outside the subscription and immediately after the result of the observer you can use RxJs .toPromise() to use it as a promise and change the method to async:
async callerFn(){
const response = await this.repository.getById(Ids).toPromise();
this.newIds = response.map((id) => {
return id;
});
console.log(this.newIds);
// use your property here
}
Yes, Because the Javascript is interpreting line-by-line execution so that it will not wait for other processes to complete. That's why the last console will return undefined. In the same time if you use the console inside of the subscriber then you will get proper log because the subscriber will wait for the response and bind it with this.newIds
this.repository.getById(Ids).subscribe((response) => {
console.log(response);
this.newIds = response.map((id) => {
return id;
});
console.log(this.newIds);
});
Here I'm attaching a good read about the observable subscribe
https://betterprogramming.pub/observables-vs-promises-which-one-should-you-use-c19aef53c680
In addition to this, If you want to go with the newIds access outside of subscriber scope please use promise with async await. Here I'm adding a sample
async getAsyncData() {
this.asyncResult = await this.httpClient.get<Employee>(this.url).toPromise();
console.log('No issues, I will wait until promise is resolved..');
}
You can do like this..
your component file like below
newIds: Observable<any> = of(this.id).pipe(
concatMap((id) =>
this.getId(id).pipe(map((data) => data.map((rowId) => rowId.id)))
)
);
getId(id: any) {
return of([{ id: 1 }, { id: 2 }, { id: 3 }]);
}
your html file like below, and use async pipe for subscription. here you can use concateMap pipe rxjs operator to sequentially calling observable and then assing value to your newId varible.
<pre>
{{ newIds | async }}
</pre>
Demo in this live link Stackblitz Link
I would approach in a different way: if you have to remap the value you can use map operator:
this.repository.getById(Ids)
.pipe(map(response) => response.map(id => id))
.subscribe((id) => {
console.log(response);
this.newIds = id;
});
Actually, I don't understand why you need to map a value that you already have, but I think this is the clear solution.

Fetch is done after statements in .then

I have component which is fetching data from server, and then passing result to another component.
getData(args) {
return fetch(`${process.env.REACT_APP_API}/offers?title=${args}`)
.then(response => response.json())
.then((services) => { this.setState({ services }); });
}
In child component I want to pass url in callback function and then do something with result (in this case console.log services), but console.log is firing before fetching data is over.
handleSubmit(event) {
event.preventDefault();
if (this.state.validPhrase === false) return;
this.props.updateSearchPhrase(this.state.searchPhrase);
this.props.onSubmit(this.state.searchPhrase)
.then(console.log(this.props.services));
}
How can I fix this?
then(console.log(this.props.services));
You're immediately calling the console here. You need to enclose it in a function:
then(() => console.log(this.props.services));

Console.log shows as undefined after subscribing to provider in Ionic3

I'm trying to get data using the Spotify api in an Ionic 3 app that I'm building, but for some reason when I try to console.log the data, it is showing as undefined. Let me show some code and then explain further. Here is my getData():
getData(){
console.log("getData has been called!!!");
return this.http.get(this.dataUrl).map((res) => {
res.json(),
console.log(res.json())//this works fine
});
}
The above code works fine. It is in a provider/service called 'soundData'. The problem is with the following bit of code. When I try to console.log the data, it shows as undefined in my browser:
ionViewDidLoad(){
this.soundData.getData().subscribe(
returnedData=> {
this.data = returnedData;
console.log(this.data) //this shows as undefined
},
returnedError => {
this.error = returnedError;
});
}
I don't really see what I'm doing wrong. It seems that everything should be working fine, but maybe I'm missing something since I'm new to TypeScript and Ionic.
You need to return from .map
getData() {
console.log("getData has been called!!!");
return this.http.get(this.dataUrl).map((res) => {
console.log(res.json());//this works fine
return res.json();
});
}
A more idiomatic way would be to use the .do method to perform the logging operation separately as in
getData() {
return this.http.get(this.dataUrl)
.map(res => res.json())
.do(json => console.log(json));
}
You can simply follow the below pattern.
Your service method
getData(): Observable<your-data-type>{
return this.http.get(this.dataUrl).map(res => res.json());
}
Your subscribe method
getData(): your-data-type {
this.soundData.getData().subscribe(
returnedData => {
this.data = returnedData;
},
err => {},
() => {}
);
}

How to make Observable.flatMap to wait to resolve

Playing with RxJS and React, I'm having problem of how to wait for data in Observable.fromPromise generated within map on another Observable.
I have async helper:
const dataStreamGenerator = (url = CLIENTS_DATA_URL) => (
Rx.Observable.fromPromise(fetch(url))
.flatMap(response => Rx.Observable.fromPromise(response.json()))
.catch(err => Rx.Observable.of(new Error(err)))
);
Then I have actions.fetchClients$ which is Rx.Subject:
actions.fetchClients$.map((url = CLIENTS_DATA_URL) => {
const ts = Date.now();
console.log('FETCH CLIENTS with: ', url);
return dataStreamGenerator(url);
}).map(val => {
console.log('GOT DATA IN REDUCER: ', val);
const error = (val instanceof Error) ? val.message : undefined;
const data = error ? undefined : val;
actions.receivedClientsData$.next({ data, error, ts: Date.now() });
return (state) => state;
})
(Yes, trying to mimick Redux in RxJS).
Whan I test the dataStreamGenerator, it works ok (with ava) and delivers data:
test('dataStreamGenerator', t => {
const ds$ = dataStreamGenerator(CLIENTS_DATA_URL);
return ds$.do((data) => {
t.is(data.length, 10);
});
});
(AVA automatically subscribe to observable and consume it, so there is no need to subscribe).
But the actions.fetchClients$.map((url = CLI... second map (starting... console.log('GOT DATA IN REDUCER: ', val);... is still getting the Observable and not the data from dataStream$.
I tried all possible combinations of map and flatMap in fetchClients$ code but still no luck.
My test code is:
test.only('fetchClients$', t => {
const initialState = {};
actions.fetchClients$.next('http://jsonplaceholder.typicode.com/users');
reducer$.subscribe(val => {
console.log('TEST SUBSCRIBE VAL: ', val);
t.pass();
});
actions.fetchClients$.next('http://jsonplaceholder.typicode.com/users');
});
I cant figure out how to wait to the Observable dataStreamGenerator(url); to emit data and not the Observable.
Thanks.
You need to flatten the results of what you returned from dataStreamGenerator.
actions.fetchClients$
//This is now a flatMap
.flatMap((url = CLIENTS_DATA_URL) => {
const ts = Date.now();
console.log('FETCH CLIENTS with: ', url);
return dataStreamGenerator(url);
});
The map operator delivers the value as-is down stream, whereas flatMap will flatten Observables, Arrays and Promises such that their values are what get propagated.
It works in the test case because you are directly subscribing to the Observable returned by dataStreamGenerator.

Categories

Resources