I am having an interesting issue. The general idea of what I am doing is pulling data from a Firebase database, and populating a table based on that data. Everything runs perfectly during initial population--cells and rows are populated as they should be, but the weird issue is that the scripts seem to execute again randomly. I've logged the incoming data to the console, and can see it print twice after some amount of time.
This second execution does not happen if I am to navigate between pages, or reload the page--in either of those cases everything works as it should. The problem SEEMS to happen when I log back into my computer after locking it??? Does anybody have ANY idea what could be going on here? Relevant portion of script below:
const table = document.getElementById('myTable');
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
let user = firebase.auth().currentUser;
let uid = user.uid;
const dbRef = firebase.database().ref().child("data/" + uid);
dbRef.once('value', snap => {
var dataCount = snap.child("secondData").numChildren();
var datalist = snap.child("secondData").val();
var dataArray = Object.keys(datalist).map(function(k) {
return datalist[k]
});
pullAllInfo(dataCount, dataArray);
});
}
});
function pullAllInfo(count, array) {
let k = 0;
let dataArray = [];
for (i = 0; i < count; i++) {
let specificRef = firebase.database().ref().child("secondData/" + array[i]);
specificRef.once('value', snap => {
var optionsTag = array[k];
k++;
var dataId = snap.child("id").val();
var dataName = snap.child("name").val();
var dataCount = snap.child("data").numChildren();
dataArray.push(dataId, dataName, dataCount, optionsTag);
if (k == count) {
buildTable(dataArray);
console.log(dataArray);
}
});
}
}
As you can see from the code above I AM calling .once() for each reference, which would prevent data duplication from the typical .on() call. Just cant seem to figure this one out. ALSO I have an iMac, just for anyone curious about my potential computer unlock diagnosis.
Thanks all!
Most likely, the auth state is changing and setting off your function. Try throwing a log under firebase.auth().onAuthStateChanged like this:
firebase.auth().onAuthStateChanged(firebaseUser => {
console.log( 'auth state changed', firebaseUser );
if (firebaseUser) {
My guess is that you'll see that the AuthState is changing when you log out/log in from your computer.
I solved this issue by creating another global boolean called preLoaded. At the beginning, it is set to false and, once the data is loaded and passed off to build the table, it is set to true. It now looks like this:
if(k == count && preloaded == false){
preloaded = true;
console.log(dataArray);
buildTable(dataArray);
}
All set!
Related
I have an issue in which users aren't updating case status when saving cases. The status field is required and usually always populated. I have a requirement from the client to prompt the user to make sure the case status is updated and correct. Without building a bunch of code for this, I leveraged a form, made everything required not visible (it will be there anyways initially) and only made the case status fields visible.
I did this so that it's always navigated to OnSave as seen as follows:
The following is the JS:
function OnSave(context)
{
debugger;
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
var formContext = formContext;
if (formContext.ui.getFormType()==2)
{
var lblForm = "Case Status Modal";
if (formContext.ui.formSelector.getCurrentItem().getLabel() != lblForm)
{
var items = formContext.ui.formSelector.items.get();
for (var i in items)
{
var item = items[i];
var itemId = item.getId();
var itemLabel = item.getLabel()
if (itemLabel == lblForm)
{
item.navigate();
}
}
}
}
}
The problem here is when I navigate here:
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
formContext.data.entity.save();
The formContext.data.entity.save();kicks the OnSave() off again and the user gets a prompt asking them to save again when they already saved. Totally kills the whole flow.
So I thought I'd create an OnSave helper variable like so: var saveCounter = 0;
I immediately knew this would cause probs.
Then I found this: https://learn.microsoft.com/en-us/power-apps/developer/model-driven-apps/clientapi/reference/save-event-arguments/getsavemode
The problem here is that it doesn't same to tell me if the user executed this or was this kicked off by JS? -- The OnSave function.
Am I thinking to hard? am I missing something? any extra eye would help.
Regards,
I suppose .save() to be async... That is why I suggest a setTimeout() below. Anyway, using a global flag to make an exception on the programmatic save is a way to investigate.
let savingProgramatically = false
function OnSave(context)
{
debugger;
// return if the flag is on
if(savingProgramatically) return
var formContext = context.getFormContext();
var saveCounter = 0;
LoadCaseStatusModal(formContext, saveCounter);
}
function OnLoad(context)
{
var formContext = context.getFormContext;
}
function LoadCaseStatusModal(formContext, saveCounter)
{
debugger;
// Set the flag true
savingProgramatically = true
// save
formContext.data.entity.save();
// set the flag back to false asap -- Adjust the delay
setTimeout(()=>{savingProgramatically = false}, 100) // maybe 0 delay works... Or no setTimeout at all.
...
I am new to programming and I am trying to implement a picture gallery in which you can like/vote your favorite pictures. Each picture displays a counter of the number of likes/votes.
The caveat is that while you can vote for different pictures, you can only vote once for each one.
Any ideas on how to achieve this?
const dCounters = document.querySelectorAll('.CountLike');
[].forEach.call(dCounters, function(dCounter) {
const el = dCounter.querySelector('button');
const cId = dCounter.id;
const dDatabase = firebase.database().ref('Like Number Counter').child(cId);
// get firebase data
dDatabase.on('value', function(snap) {
let data = snap.val() || 0;
dCounter.querySelector('span').innerHTML = data;
});
// set firebase data
el.addEventListener('click', function() {
dDatabase.transaction(function(dCount) {
return (dCount || 0) + 1;
});
});
});
I have tried using local storage to check if user has voted already =
localStorage.getItem('iHaveVoted', 'yes')
but this doesnt get into account until browser is refreshed. Therefore, user is still allowed to increase the votes multiple times.
I'm writing a cloud function that uses request-promise and cheerio to scrape a website and then check that information against a user document.
I am not entirely familiar with Javascript and Cloud Functions.
I've come so far that I managed to extract the information I need and navigate to the user's document and compare the data. Now the last piece of this function is to give the user points for each matching data point, so I need to update a map inside the user document.
This function has to loop through all users and change their document if the data point matches. I'm not sure the way I've written the code is the most optimal in terms of performance and billing if the userbase gets huge... Any pointers to how I could minimize the impact on the task would be of great help, as im new with JS.
So this is the code:
exports.getV75Results = functions.pubsub.schedule('every 2 minutes').onRun(async (context) => {
let linkMap = new Map();
const url = `https://www.example.com`
const options = {
uri: url,
headers: { 'User-Agent': 'test' },
transform: (body) => cheerio.load(body)
}
await rp(options)
.then(($) => {
for(let i = 1; i <= 7; i++)
{
//Find player from game
const lopp1 = $(`#mainContentHolder > div > div.mainContentStyleTrot > div > div.panel-body > table:nth-child(1) > tbody > tr:nth-child(${i}) > td:nth-child(2) > span`).text()
const lopp1StrR1 = lopp1.replace("(", "");
const lopp1StrR2 = lopp1StrR1.replace(")", "");
const lopp1StrR3 = lopp1StrR2.replace(" ", "");
linkMap.set(i, lopp1StrR3.toUpperCase());
}
console.log(linkMap);
return linkMap;
}).then(async () => {
//Start lookup users
let usersRef = db.collection('fantasyfotball').doc('users');
usersRef.listCollections().then(collections => {
collections.forEach( collection => {
var user = collection.doc(collection.id);
let batch = new admin.firestore().batch();
user.get().then(function(doc) {
let json = doc.data();
//Look in users collection if players document exist
Object.keys(json).forEach((name) => {
if(name != null) {
//Document with users active fotball players
if(name == 'players') {
let i = 0;
Object.values(json[name]).forEach((value) => {
i++;
if(value.localeCompare(linkMap.get(i)) == 0) {
//Loop through user keys and find owned players if user has the correct player
Object.keys(json).forEach((map) => {
if(map != null)
{
//Document with a map of player owned fotball players, each respective player has a key = 'fotball player' and value = '[price, points]'
if(map == 'ownedplayers')
{
Object.entries(json[map]).forEach((players) => {
if(players[0].localeCompare(value) == 0) {
console.log(players[1][1]);
//Add points to respective player field
//PROBABLY NOT HOW TO CHANGE A DOCUMENT FILED, THIS DOESNT WORK..
players[1][1]++;
}
});
//EACH TIME THIS RUNS IT SAYS: "Cannot modify a WriteBatch that has been committed"
batch.update(user, {'ownedplayers': json[map]});
}
}
});
}
});
}
} else {
console.log('user does not have a playermode document.');
}
});
});
return batch.commit().then(function () {
console.log("Succesfully commited changes.");
return null;
});
});
});
}).catch((err) => {
return err;
});
});
The issues i get in the console are "Cannot modify a WriteBatch that has been committed." and I fail to modify and add points to the player field inside the users document.
This is the console:
This is the firestore document structure:
I'm completely stuck on this.. Feels like I've tried all different approaches, but I think i dont fully understand cloud functions and javascript, so i would gladly recive feedback and help on how to make this work.
Cheers,
Finally.... i managed to update the document successfully. I put the commit outside another ".then()". Thought I tried that, but yay I guess :P
}).then(() => {
return batch.commit().then(function () {
console.log("Succesfully commited changes.");
return null;
});
The problem now is that it commits every loop. I think the most optimal here would be to batch update ALL users before committing?
And again, is there a more optimal way to do this, in terms of minimizing the operation and impact? I'm afraid I go too deep with for loops instead of directly navigating to the document, but haven't found an easier way to do that.
Any thoughts?
Context:
I 'm doing a cloud function to send pushes to multiple users. I need to recover the info of each user to know some data like, name, country..etc..
Problem:
Actually I recover the list of user Id's and when I got it, then I create an array of promisesto recover all the info:
var usersPromises = []
for (var i = 0; i < usersInRange.length; i++) {
usersPromises[i] = firestore.collection("users").doc(usersInRange[i])
}
Then I recover and send the push using firestore.getAll():
firestore.getAll(...usersPromises).then(results => {
for(snapshot in results){
if(snapshot.exists){
......
var user = snapshot.data()
......
}else{
......
}
}
})
This solution is actually working "fine" almost all the time. But at this moment the Firestore db has some users that do not exist or something is wrong, because the method getAll()stops before finishing all the promises. I know it because no push is sent, and in the console, just say that the method has finished.
Reading in SO and documentation, I saw, that getAll stops if some promise is "broken". (all or nothing)
And here is where I'm lost. How can I "force" or do in another way, to just "jump" this promises that can't be completed?
P.S:
I tried to do with a "for" but It seems to omit some promises:
for (var i = 0; i < usersPromises.length; i++) {
usersPromises[i]
.get()
.then(snapshot => {
if(snapshot.exists){
......
var user = snapshot.data()
......
}else{
......
}
})
}
I think its not a problem of getAll. I have tested like this:
const firestore = new Firestore();
let doc = []
doc[0] = firestore.doc('test/test');
doc[1] = firestore.doc('test/test1');
doc[2] = firestore.doc('test/doc');
firestore.getAll(...doc)
.then(result=> result.forEach(doc => console.log(doc._fieldsProto)))
.catch(err=>console.log(err));
In my database I have 'test/test' and 'test/doc' document, but I do not have 'test/test1' and results look like this:
So we just get undefined on document that is not exist and that's all. I suggest to add catch and see if there is any exception. When I have been writing the test the function was interrupted by typo mistake in inner function.
I hope this will help!
It seems like nothing is stored in the array with this code:
(Assume showList is an actual div id; didn't think it was necessary to include those tags in the code)
var i = 0;
var array = [];
var showList = document.getElementById('showList');
firebase.firestore().collection('TV Shows').get().then(snapshot => {
snapshot.forEach(doc => {
array[i] = doc.data().show.name;
i++;
//console.log(doc.data().show.name);
});
});
showList.innerHTML = array[4];
Funny enough, that commented console.log line works fine and displays all the objects, so I don't understand why I can't store it in the array. A big part of me feels as if it's my direction in using the array and/or i variable, but I tagged it as a firebase/firestore issue as well on the off chance it turns out to be a firebase issue.
If someone could point me in the write direction, I would appreciate it.
.get() is asyncronyous, and at the time you run:
showList.innerHTML = array[4];
array has not been initialized yet.
You either need to use callbacks/promises as mentioned in the linked duplicate, or simply move your call inside the .then() function:
var showList = document.getElementById('showList');
firebase.firestore().collection('TV Shows').get().then(snapshot => {
var array = [];
var i = 0;
snapshot.forEach(doc => {
array[i] = doc.data().show.name;
i++;
//console.log(doc.data().show.name);
});
showList.innerHTML = array[4];
});