Accessing method vars from inside arrow function - javascript

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++;
});
});

Related

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

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);
});
};

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.

can't get setState inside a firebase function out of it

first i set listaa as a global variable
i'm trying to take out the variable(array) "listaa" from the firebase function that sets it... I'm plannig to use it later to make some maths.
I used console.log(listaa) inside the firebase function and it worked,
but then when i try to use it() outside(the firebase function) it doens't work.
I tried to use setState to make it usable outside but didnt work too
if (funcionar == 0) {
//============================================================== ANTES ===================================================
var newRef = firebase
.database()
.ref()
.child("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
});
var fire = database.ref("EstoqueCadaVendedor/" + vend);
fire.once("value", snap => {
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
console.log("items:");
console.log(items);
for (j = 0; j < prod.length; j++) {
// var a = parseInt(snap.val().quantidade);
console.log("d: " + items[j].data);
listaa.push(items[j].data);
}
// this.setState({ lista:listaa })
// console.log("lista:");
// console.log(this.state.lista[2]);
console.log("listaa");
console.log(listaa);
});
console.log("listaaaaaaaaaaaaaa");
console.log(listaa);
Ill use 'listaa' here:
for (i = 0; i < prod.length; i++) {
firebase
.database()
.ref()
.child("EstoqueCadaVendedor/" + vend + "/")
.update({
[prod[i].key]: parseInt(quantNova[i]) + listaa[i]
});
}
// this.setState({ quantidade: [], vendedor: "" });
}
Tuan over here.
The reason why you don't see it is because your code is asynchronous, so when you print console.log() at the end. The firebase calls haven't finished yet.This is a typical race condition scenario.
FYI, I personally prefer to use the promise style rathen than the callback, I think it makes the code cleaner and easier to read. Try this:
let firebaseDB = firebase.database();
if (funcionar == 0) {
return firebaseDB.ref("TransferenciasPraVendedores/")
.push({
vendedor: vend,
dia: functions.dataHoje(),
hora: functions.horaHoje()
})
.then(() => {
return firebaseDB.ref("EstoqueCadaVendedor/" + vend).once('value');
.then(snapshot => {
let snap = snapshot.val();
items = [];
snap.forEach(data => {
items.push({
key: data.key,
data: data.val()
});
});
return {snap, items};
})
.then({snap, items} => {
for (j = 0; j < prod.length; j++) {
listaa.push(items[j].data);
}
//console.log(lista)// NOW your lista should have all the items
// I would dispatch your redux action here.
})
.catch(error => {
throw error
}
Let me know if it helped. I can look into it more in depth. Saludos :)
Did you review the order of the log messages ? if you look carefully, you will recognize the data gets available after later in the process. You probably need to use async/promise to wait until the data gets available or you can use a call back function
async
function to make sure the data is available in the list before you use it.
An async function can contain an await expression, that pauses the
execution of the function and waits for the passed Promise's
resolution, and then resumes the async function's execution and
returns the resolved value.
In the example below, I am passing a call back function that gets called when data is available :
export function listReleases(callback) {
//console.log("hello from list releases ");
releasesRef.once("value").then(
function(snapshot) {
const releases = snapshot.val();
if (releases) {
callback("releases", releases);
}
},
function(error) {
// The Promise was rejected.
console.error(error);
}
);
}
+++++++++++++++++++++++++++
//call the function to get the data and pass a call back function
releases = listReleases(this.myCallback);
myCallback = (key, value) => {
//do whatever you need to do in here
};

Chained and Nested promises with For Loop

I am trying to get each property of my games within chained promises (Each of the property is coming from a different async calls).
Logic of my algorithm:
Check the Network and Get the smart contract address
Register the contract containing the addresses of all the Games
Get the number of Games
For each game, perform one aSync call
per property
Print all the games and details (here I am not able
to get the updated object)
Code:
var games = [];
window.addEventListener('load', function() {
// Check the Network and assign the smart contract address
web3.eth.net.getId()
.then(function(networkId) {
let contractAddressRegistry;
if (networkId == 1) {
contractAddressRegistry = "0xQWERTYUIOPQWERTYUIOPQWERTY"
} else {
contractAddressRegistry = "0x12345678901234567890123456"
}
return contractAddressRegistry;
})
.then(function(contractAddressRegistry) {
let contractRegistry = new web3.eth.Contract(contractAbiRegistry, contractAddressRegistry);
contractRegistry.methods.numberOfGames().call()
.then(function(numberOfGames) {
for (let i = 0; i < numberOfGames; i++) {
let game = {};
game.propertyA = aSyncCallGetPropertyA(i); // Promise
game.propertyB = aSyncCallGetPropertyB(i); // Promise
game.propertyC = aSyncCallGetPropertyC(i); // Promise
}
games.push(game);
})
})
.then(function() {
console.log(games) // Empty
})
})
I tried used Promises.all() but I am not able to sync it properly as some async calls are within a then().
How can I make sure to get the object Games filled with all its properties?
You should use Promise.all like this. Basically, you need to wrap all three aSyncCallGetProperty async calls in Promise.all for waiting until they really finish then assign the results to object game.
whatever
.then(function(contractAddressRegistry) {
let contractRegistry = new web3.eth.Contract(contractAbiRegistry, contractAddressRegistry);
return contractRegistry.methods.numberOfGames().call();
})
.then(function(numberOfGames) {
return Promise.all(numberOfGames.map(() => {
return Promise.all([
aSyncCallGetPropertyA(),
aSyncCallGetPropertyB(),
aSyncCallGetPropertyC()
]).then(results => {
let game = {};
game.propertyA = results[0];
game.propertyB = results[1];
game.propertyC = results[2];
return game;
});
}));
})
.then(function(games) {
console.log(JSON.stringify(games));
})
#Lewis' code seems right but I can not make sure what numberOfGames is. Assuming that it's an integer as used in your question (not an array as treated in the other answer) here is a further rephrased version without nested .then()s.
window.addEventListener('load', function() {
web3.eth.net.getId()
.then(networkId => networkId === 1 ? "0xQWERTYUIOPQWERTYUIOPQWERTY"
: "0x12345678901234567890123456")
.then(contractAddressRegistry => new web3.eth.Contract(contractAbiRegistry, contractAddressRegistry).methods.numberOfGames().call())
.then(numberOfGames => Promise.all(Array(numberOfGames).fill()
.map(_ => Promise.all([aSyncCallGetPropertyA(),
aSyncCallGetPropertyB(),
aSyncCallGetPropertyC()]))))
.then(function(games){
games = games.map(game => ({propertyA: game[0],
propertyB: game[1],
propertyC: game[2]}));
doSomethingWith(games);
});
});

Firebase Function returning Undefined values, even with promises

I'm new on Firebase, I've a simple program that fetches product information using "ProductID".
Here's the hierarchy:
PRODUCT CHILD
The problem is, when I use this function, it returns undefined values. I know it's asynchronous, and I've tried adding promises, but still in vain. Here's it:
function getProduct(prID){
var db = admin.database();
var ref = db.ref("server/products/" + prID);
let rData = '';
ref.on("value", function(snapshot) {
rData = snapshot.val();
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return rData;
Calling the same function with same values returns the product, but calling for different parameter values, it returns undefined.
I've tried using (in a function getProductPromise(prID) ), I changed above function to:
function getProductPromise(id) {
var db = admin.database();
var ref = db.ref("server/products/" + id);
let rData = '';
return ref.once('value').then(function(snapshot) {
return snapshot.val();
});
}
it is still returning undefined values. What could be the possible way to ensure that some values are returned.
Thanks.
Looks like you're having some scoping issues in the first example, so not sure if it would ever work as you're expecting. Promises are your best bet. You can try something like:
function fetchProductById (id) {
var db = admin.database()
var collectionRef = db.ref('server/products')
var ref = collectionRef.child(id)
return ref.once('value')
.then((snapshot) => {
return snapshot.val()
})
}
// then to implement:
fetchProductById('abc')
.then((result) => {
console.log('the result is', result)
})

Categories

Resources