how to deal with firebase onsnapshot as an object or array? - javascript

im doing orders onsnapshot from firebase but im confused how to deal with it, I get an [] when I log it out but when I logout the type of orders it says object,
I'm trying to add the id of each order and place it inside the array orders along with the existing data but its not working , if I
console.log(orders[0]) I get undefined and if I treat it as an object orders['0'] i also get undefined.
here is the code:
im using vuejs mounted
async mounted() {
let id = [];
let orders = [];
await db.collection("orders").onSnapshot(doc => {
doc.docs.forEach(x => {
id.push(x.id);
orders.push(x.data());
});
});
for (order in orders) {
let i = 0;
order.id = id[i];
i++
}
console.log(orders);
}}
the result of this code is the same orders array without any change at all.

Data is loaded from Firestore asynchronously. While that is happening, your main code continues to execute, so that the user can continue to use the app.
What this means in practice is that in your code the console.log(orders) is executed before any of the orders.push(x.data()) is ever run.
The solution is pretty simple: any code that needs the data from the database, needs to be inside the callback that is called with the data:
async mounted() {
let id = [];
let orders = [];
db.collection("orders").onSnapshot(doc => {
doc.docs.forEach(x => {
id.push(x.id);
orders.push(x.data());
});
for (order in orders) {
let i = 0;
order.id = id[i];
i++
}
console.log(orders);
});
}
}
Alternatively, you can use async and await to handle the asynchronicity, but in that case you should use get instead of onSnapshot and not use a callback:
async mounted() {
let id = [];
let orders = [];
const snapshot = await db.collection("orders").get();
snapshot.docs.forEach(x => {
id.push(x.id);
orders.push(x.data());
});
for (order in orders) {
let i = 0;
order.id = id[i];
i++
}
console.log(orders);
}

You should check first, returning snap is either object or array. Generally I found it return object. Hence you should do like.
mounted = async () => {
let id = [];
let orders = [];
db.collection("orders").on("value", async snapshot => {
let ordersData = snapshot.val();
if (ordersData !== null) {
Object.values(ordersData).map(item => {
orders = [...orders, item.data]; //map according to your data
});
}
console.log("orders===>", orders);
});
};

Related

.push() is not working outside of the for loop

I have already handled a response from an API call. I then export the data into a function that i'm using a for loop inside. The const diomerda is passed to the ejs file where i am outputting the array data from the API. However the data in the array is over 5000 entries, so i want to run a for loop to populate the ejs file with the entire array. But when I use loop.push it does not populate the const loop. console.log(loop) just reads [].
Once the const loop = [] is populated with the array i was then going to pass the data into the const diomerda = [json.entries[loop].character.name]
I'f anyone has any other ideas on how to acheive what i'm trying to do then please feel free to send away. Thanks in advance.
.then(response => response.json())
.then(json => ejsoutput(json))
}
function ejsoutput(json) {
const loop = []
console.log(loop)
const diomerda = [json.entries[0].character.name, json.entries[1].character.name, json.entries[2].character.name]
for (var i = 0; i < json.entries.length; i++) {
loop.push(i)
}
res.render('index', {
leaderboard: diomerda
})
}
});
Once the const loop = [] is populated with the array i was then
going to pass the data into the const diomerda = [json.entries[loop].character.name]
If what you want is to populate diomerda with each character name return from the api, do this
function ejsoutput(json) {
const diomerda = [];
for (var i = 0; i < json.entries.length; i++) {
diomerda.push(json.entries[i].character.name);
}
console.log(diomerda);
}
OR in simpler terms
function ejsoutput(json) {
const diomerda = json.entries.map(item => item.character.name);
console.log(diomerda);
}
To debug, print (log) the json object, instead of the empty loop array (shoule get you on track).
Json should be a promise. That's probably what you're running into.
See: https://developer.mozilla.org/en-US/docs/Web/API/Response/json
Try something like
json().then(function(json) {
});

Get array from function

I have tried to get the array I can log inside the function to be used outside. I’m using sapper. Have been stuck with this for multiple hours now, so it was time to reach out to the community!
let dvd = [];
let artistName = [];
let searchString = [];
onMount(async () => {
const res = await fetch(`artists/all`);
const data = await res.json();
const newdata = data.map(x => {
if (x.titles.length > 0) {
dvd.push(x.titles[0].graphics.dvd)
artistName.push(x.titles[0])
}
})
})
let searchTerm = '';
async function getData() {
searchString.push(artistName.filter(d => d.artistName === searchTerm));
console.log(searchString)
}
If I understand the question correctly, your issue is that the updated value of searchString is not being applied to the DOM. This is due to how Svelte's reactivity works. From the tutorial:
Because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.
You should update getData to assign to searchString instead of calling push.
async function getData() {
searchString = [...searchString, artistName.filter(d => d.artistName === searchTerm)];
console.log(searchString)
}

How do you turn a "for" into an async mode?

export function RecommendList(data) {
return (dispatch, getState) => {
let db = loadFB().firestore();
let query = db.collection('users').where("recommend", ">", "0").orderBy("recommend", "asc")
let user_list = [];
let uid_arr=[];
let result = [];
query.get().then(async docs => {
docs.forEach(doc => {
const recommender = doc.data();
const recommend_id = recommender.recommend;
const recommend_person = recommender.displayName;
user_list.push({id : recommend_id, recommend_person : recommend_person });
})
uid_arr = getRecommendList(user_list);
console.log("getRecommendList",uid_arr);
for(let i = 0; i < uid_arr.length; i++) {
const user_doc = await db.collection('users').doc(uid_arr[i].id).get();
console.log(user_doc.data());
let user_info = user_doc.data()
user_info.betball_dupli_count = uid_arr[i].count;
user_info.recommend_person = uid_arr[i].person;
console.log('displayname' , user_info.displayName , 'betball count',user_info.betball_dupli_count,'person',user_info.recommend_person);
result.push(user_info);
}
console.log('result!', result);
dispatch({
type: types.SET_USER_LIST,
data: result,
page: 1
})
})
}
}
I work on importing data from the Fire Store and sending it to the dispatch.
By the way, I want to make a code that increases the efficiency of this work by asynchronous mode in javascript. I'd like to know how to wait and process a form asynchronously.
In short, how to turn "for" of this code into async mode!
With minimal changes to your existing code:
let userQueries = [];
for (let i = 0; i < uid_arr.length; i++) {
userQueries.push(
db.collection('users').doc(uid_arr[i].id).get()
.then(user_doc => {
console.log(user_doc.data());
let user_info = user_doc.data()
user_info.betball_dupli_count = uid_arr[i].count;
user_info.recommend_person = uid_arr[i].person;
console.log('displayname', user_info.displayName, 'betball count', user_info.betball_dupli_count, 'person', user_info.recommend_person);
result.push(user_info);
})
)
}
await Promise.all(userQueries);
The outer for loop will just initiate the queries and the then part of each one will run when the specific query is complete. Outside of the for loop, the Promise.all call will wait for all of the queries to complete before continuing.

Accessing method vars from inside arrow function

In my vuetify/vue.js appliction, i'm trying to read a firestore collection I've stored to calculate an average rating from all ratings stored there:
function updateRating() {
let firestoreQuery = firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id);
let ratingsSum = 0;
let ratingsAmountCounter = 0;
firestoreQuery.get().then(querySnapshot => {
querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
});
}
However, I can't access the ratingsSum and ratingsAmountCounter variables from inside my forEach arrow function. I've already tried using this or the var _this = this workaround, but both don't seem to work and eslint still reports:
'ratingsSum' is assigned a value but never used (no-unused-vars)
What am I doing wrong?
As the comments suggest, the problem here is that you have no code that actually reads ratingsSum and ratingsAmountCounter. The error message is telling you that ESLint has triggered on the rule called "no-unused-vars" to tell you about this. Variables that are never read are probably a bug, or at least unnecessary code.
What you should probably do is return a promise to the caller so that they can get a hold of the values, whenever the asynchronous get() is complete and the values are computed. For example:
function updateRating() {
let firestoreQuery = firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id);
let ratingsSum = 0;
let ratingsAmountCounter = 0;
return firestoreQuery.get().then(querySnapshot => {
querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
return { ratingsSum, ratingsAmountCounter }
});
}
The caller of this function now receives a promise that resolves when the values are known, and they can be fetched out of the resolved object:
updateRating().then(result => {
const { ratingsSum, ratingsAmountCounter } = result;
// Now you can use ratingsSum and ratingsAmountCounter...
})
this might work, I have no way to test it since I don't know how to use vue but here goes
async function getData(){
return await firebase
.firestore()
.collection('ratings')
.where('qrCodeId', '==', this.qrCode.id)
.get()
}
})
you can then do this:
getData().then(_querySnapshot => {
var ratingsSum = 0
var ratingsAmountCounter = 0
_querySnapshot.forEach(doc => {
ratingsSum += doc.rating;
ratingsAmountCounter++;
});
});

How to access multiple documents from Firestore in a cloud function

I have a cloud function that is triggered on a document write. The cloud function needs to check multiple documents based on the trigger and execute if/else statements.
I've created a function that accesses all documents with a Promise.all, but this errors when trying to access all the document information if not yet available.
export function onTriggered(change, context) {
const userPrevData = change.before.data();
const userNewData = change.after.data();
const promises = [];
// Get the uid
const uid = context.params.userId;
// User DocRef
const userDoc = firestoreInstance.collection('users').doc(uid).get();
// User Session DocRef
const userSessionDoc = firestoreInstance.collection('sessions').doc(uid).get();
// Solution DocRef
const solutionDoc = firestoreInstance.collection('solution').doc('solutionId').get();
promises.push(userDoc, userSessionDoc, solutionDoc);
return Promise.all(promises).then((snapshots) => {
// Use Promise.all with snapshot.docs.map to combine+return Promise context
return Promise.all(snapshots.map((doc) => {
// At this point, each document and their Ids print to the console.
console.log('docId:::', doc.id);
console.log('docData:::', doc.data());
const solutionDocData = getSolutionDocData(doc);
// This will print as 'undefined' until the correct document data is processed
console.log('solutionDocData:::', solutionDocData);
// This will print as 'undefined' until the correct document data is processed
const clientSeed = doc.get('clientSeed');
// Check to see if the Users' Guess is the same as the solution
if (userNewData.guess.color === solutionDocData.guess.color && userNewData.guess.number === userNewData.guess.number) {
console.log('User solution is correct');
}
}));
})
}
function getSolutionDocData(doc) {
if (doc.id === 'solutionId') { return doc.data(); }
}
I expect 'User solution is correct' if the condition is satisfied. But, I get an error because data is undefined.
The solution was to move most of the logic a .then()
return Promise.all(promises).then((snapshots) => {
// Use Promise.all with snapshot.docs.map to combine+return Promise context
return Promise.all(snapshots.map((doc) => {
// At this point, each document and their Ids print to the console.
console.log('docId:::', doc.id);
console.log('docData:::', doc.data());
return doc.data();
})).then(data => {
console.log('data:::', data);
let userDocDetails = {};
let userSessionDocDetails = {};
let solutionDocDetails = {};
data.forEach(document => {
if (document.uid === uid) { userDocDetails = document }
if (document.serverSeedEncrypted) { userSessionDocDetails = document }
if (document.solutionId) { solutionDocDetails = document }
});
});
})
I am unsure if the data will always be returned in the order of the original promise array, so I used a forEach statement to identify unique properties and assign them accordingly.

Categories

Resources