i get two different values when i console.log the same array - arrays

I have a function that gets a product from my firstore database with the id.
when i console.log the array in the subscribe i get :
console.log(product[0]) -> Object { id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }
console.log(product) -> [{ id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }]
that's good and normal but when i do a console.log outside the subscribe.
Where i want to return the array i get different values.
then i get :
console.log(product[0]) -> undefined
console.log(product) -> [{ id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }]
This is my code for getting the data and returning it
getProductfromDBByID(id: number): Observable<any> {
let product: Array<any> = [];
this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
product.push(doc.data());
console.log('product in subscribe ', product[0]);
});
});
console.log('product return value', product[0]);
return of(product);
}

That is because the console.log outside of the subscribe is executed before the subscribe is hit (asynchronous operation, request in this case).
To resolve that, move the actual subscription to the function that calls this function.
getProductfromDBByID(id: number): Observable<any> {
return this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get();
}
...
product: Array<any>;
otherFunction() {
getProductfromDBByID(/*some id*/)
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.product.push(doc.data());
});
});
}

Add "async" keyword before the function name and add "await" keyword before the future method which is the "this.firestore......"
async getProductfromDBByID(id: number): Observable<any> {
let product: Array<any> = [];
await this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
product.push(doc.data());
console.log('product in subscribe ', product[0]);
});
});
console.log('product return value', product[0]);
return of(product);
}

Related

return value from object function inside a map

I'm trying to run a getUserName function that is being called inside an Observable pipe, map, then another map. I can return a single value but I can't seem to map the passed array to then filter out item.name if it matches the id passed in. If I show the code maybe it will be easier to understand:
Not working:
export const fetchDesignsData = (usersArray: [Users]) => (dispatch: Dispatch<Action>) => {
console.log(usersArray);
const DESIGNS_URL = `http://localhost:5000/designs`;
dispatch({
type: "FETCH_DATA_REQUEST",
});
const responsePromise = axios.get(DESIGNS_URL);
const response$ = from(responsePromise);
response$
.pipe(
map((response) => {
const newArray: { name: string; courses: number; wales: number; last_updated: string; by: any }[] = [];
response.data.map((item: { name: any; courses: any; wales: any; updated: any; user_id_last_update: any }) => {
return newArray.push({
name: item.name,
courses: item.courses,
wales: item.wales,
last_updated: item.updated,
by: getUserName(item.user_id_last_update, usersArray),
});
});
dispatch({
type: "FETCH_DATA_SUCCESS",
payload: newArray,
});
})
)
.subscribe();
};
const getUserName = (userNumber: number, usersArray: [Users]) => {
return () => {
usersArray.forEach((item) => {
if (item.id === userNumber) {
return item.name;
}
});
};
};
Basically usersArray looks like this:
[{id: 1, name: "Walter Doe"},
{id: 2, name: "John Doe"}]
so I need to map that array then see if the item.id === userNumber, if yes, return item.name. But it just returns blank every time. Probably because its inside an Observable
You need to use filter as there you need to return array which satisfy condition.
//...
const getUserName = (userNumber: number, usersArray: [Users]) => {
return () => {
usersArray.filter((item) => item.id === userNumber)
.map(item => item.name)
});
};
};
//...
This worked in the end, but it doesn't show the item.name value in my component on page load, only if I route away and come back, something to do with my useEffect and dispatch.
const getUserName = (userNumber: number, usersArray: [Users]) => {
return usersArray.filter((item) => item.id === userNumber).map((item) => item.name);
};

How to implement the fetching of data for different categories of products to be shown in a home page using firebase on a Reactjs project

I want to get the products across different categories to be shown on the home page. The below code only fetches data from a single category. How do I go about fetching products from multiple categories. Should I use promises ? I feel like there will be a lot of redundant code. Is there a better way?
fetchProducts = async ( category )=>{
let productsRef = projectFirestore.collection("products");
let query = productsRef;
query = productsRef.where('category', '==', 'Beverage');
query.get().then((querySnapshot) => {
let products = [];
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
products.push({
name : doc.data().name,
price : doc.data().price,
prev_price : doc.data().prev_price,
discount : doc.data().product_discount,
id : doc.id,
src : doc.data().pictures[0].src,
rating: doc.data().rating
});
});
this.setState(() => {
return { products };
}, this.checkCartItems);
})
Edit: Here's the correct solution: docs - https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any
fetchProducts = async (categories) => {
let productsRef = projectFirestore.collection("products");
let query = productsRef.where('category', 'in', categories);
query.get().then((querySnapshot) => {
let products = [];
querySnapshot.forEach((doc) => {
// doc.data() is never undefined for query doc snapshots
products.push({
name: doc.data().name,
price: doc.data().price,
prev_price: doc.data().prev_price,
discount: doc.data().product_discount,
id: doc.id,
src: doc.data().pictures[0].src,
rating: doc.data().rating,
});
});
this.setState(() => {
return { products };
}, this.checkCartItems);
})
.catch(err => console.log(err));
};

why id is undefined?

I want to get the id of the doc but I got undefine when i console.log
is there a way to also include the doc id ?
this.unsubscribe = FirestoreService.getMessagesSnapshot(selected.id, selected.userId).onSnapshot((querySnapshot) => {
if(querySnapshot.size > 0) {
let tempObject = [];
querySnapshot.forEach((doc) => {
tempObject.push({
**id:doc.id,**
chatId: doc.data().chatId,
membersId: doc.data().membersId,
name: selected.name,
picRatio: doc.data().picRatio,
picUrl: doc.data().picUrl,
fileUrl: doc.data().fileUrl,
senderId: doc.data().senderId,
text: doc.data().text,
thumbUrl: doc.data().thumbUrl,
time: doc.data().time.toDate(),
videoUrl: doc.data().videoUrl,
});
});

How to access data returning from an API Subscribe method outside in Angular

This is my angular code block.
demandCurveInfo = [];
ngOnInit() {
this.zone.runOutsideAngular(() => {
Promise.all([
import('#amcharts/amcharts4/core'),
import('#amcharts/amcharts4/charts'),
import('#amcharts/amcharts4/themes/animated'),
import('#amcharts/amcharts4/maps'),
import('#amcharts/amcharts4-geodata/worldLow'),
])
.then(modules => {
this.createDemandCurve(modules);
})
.catch(e => {
console.error('Error when creating chart', e);
});
});
}
This is where i am trying get API data.
async getDemandCurveInfo(statusType: string, valueType ) {
const params = [];
params.push({code: 'StreamCode', name: 'TG_PS'});
params.push({code: 'segmentCodes', name: ['US']});
params.push({code: 'checkinFrom', name: '2019-01-01'});
params.push({code: 'checkinTo', name: '2019-12-31'});
params.push({code: 'statusType', name: statusType});
params.push({code: 'valueType', name: valueType});
return await this.dashboardServiceHandler.getSegmentDemand([], params).toPromise();
}
Inside this function i am calling the above method.
createDemandCurve(modules: any) {
const am4core = modules[0];
const am4charts = modules[1];
const am4themesAnimated = modules[2].default;
this.getDemandCurveInfo('REAL', 'TTV').then((data) => {
this.demandCurveInfo.push(data.valueOf().data);
console.log(this.demandCurveInfo[0]); <- first
});
console.log(this.demandCurveInfo[0]); <- second
}
In here i am trying to get this.demandCurveInfo[0] data outside.But my second console.log gives output like undefined.First console.log gives output like this. How could i get the console.log data outside?.
You can introduce a new method to do that
this.getDemandCurveInfo('REAL', 'TTV').then((data) => {
this.demandCurveInfo.push(data.valueOf().data);
printData(this.demandCurveInfo[0]);
});
printData(data: string){
console.log(data);
}

About setState in React

Below is a function getting messages from firebase database, but it only setState to only one message.
However, the console.log can log multiple messages in the object.
Is there anything wrong in my function?
getMessages(){
var messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
//alert(JSON.stringify(messages));
messages.push({
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
});
this.setState({
messages: messages
});
console.log('woooo'+JSON.stringify(messages));
});
});
});
}
You are setting the state inside the forEach Block. try moving it outside the iteration block
As JanneKlouman mentioned it's not good enough to remove it from the iterration block as you are doing async calls.
You can create a new array and set it in the state on each iteration, react will batch those set state calls:
function getMessages() {
var messages = [];
firebaseApp.database().ref('users/' + firebase.auth().currentUser.uid + '/userChat/' + firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message) => {
const newMessage = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
}
const nextState = this.state.messages.map(message => {
return {
...message,
user: {...meesage.user} // i think we must do this in order to break out of the reference as spreading will only work on a shallow level
}
});
this.setState({
messages: [...nextState, newMessage]
});
});
});
});
}
Try cloning the array before setting state:
getMessages(){
let messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
const message = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
};
// Clone messages
messages = [...messages, message];
this.setState({ messages });
});
});
});
}

Resources