I am building a web app with JS and firebase as my DB, but I'm struggeling to call on my gotData function to get it's return value.
var dbRef = firebase.database().ref('testing');
function reading(){
dbRef.on('value', gotData,errData) ;
}
function gotData(data){
var tests = data.val();
var keys = Object.keys(tests);
var length = keys.length;
var k = keys[lengthe-1];
console.log(k)
console.log(data)
return k;
}
function errData(err){
alert('error')
}
The code works and I get the documentID of the newest document returned, but I fail to call on this value.
I tried:
var fbid = gotData(data);
but I get an data is not defined error. From my understanding data is just used to reference to the Firebase data so I can do operations on it. What do I need to do, so that I can call the function with it's data ?
You're calling gotData() yourself at the bottom of the code in the jsbin, without passing in a parameter:
var id = gotData();
How I found this:
Put this line as the first statements inside gotData():
if (!data) debugger
Run the code in the jsbin again, which starts the debugger now.
Check the call stack (on the right hand side in the Chrome debugger) to see where the call came from.
You'll want to remove this gotData() call, and put all code that needs the data inside the gotData() functions. Also see:
Passing variable in parent scope to callback function
Firebase Query inside function returns null
Why Does Firebase Lose Reference outside the once() Function?
Is there any way to break Javascript function within an anonymous function?
Related
I am trying to passing out the value which located in the reading function of firebase which is forEach loop. Yet I can't figure out why it does not work with the following code.
var object = {};
var objectKey = objectKey ? objectKey : firebase.database().ref().child('test').push().key;
object[objectKey] = {a:"1", b:"2", c:"3"};
firebase.database().ref('test').update(object);
var objRef = firebase.database().ref('test');
var displayObj;
var total = 0;
objRef.on('value', function(snapshot) {
snapshot.forEach(function(childSnapshot){
displayObj = childSnapshot.val();
var a = displayObj.a;
var b = displayObj.b;
var c = displayObj.c;
total = a + b + c;
console.log(total); //expected result: 123
return total;
});
});
console.log(total); //I wish to set total = 123 but instead
//it return the original value, 0
For your information, the writing function and reading function are working well as I have tested through the firebase console.
Thank you in advance.
objRef.on is asynchronous, meaning it returns immediately before results are available to the callback function. The callback function is executed some time later with the snapshot of the data you've asked for.
On top of that, your return value coming from return total is being returned from the callback function, which is meaningless. It has no bearing on the value of the log line following the callback.
Going even further, the callback you passed to on() is going to be called repeatedly as the data at the location pointed to by objRef changes. This is probably not what you want. If you want a single snapshot at a location, use once() instead of on().
The bottom line is this: Realtime Database callbacks are asynchronous, so if you want to use a value from a snapshot coming from Realtime Database, deal with it only after the callback in invoked. Please read this blog to know more about why Firebase APIs are asynchronous.
I am trying to make a query to a firebase database using ionic. I want to store the the object snapshot.val() in a variable with the objective of displaying its content in my HTML code but I´m struggling with the scope of my upcomingOrders variable.
How can I update my variable inside the callback?
onFetchMyUpcomingOrders(){
var upcomingOrders = {}
const userId = this.authService.getActiveUser().uid;
var ref = firebase.database().ref("orders/confirmed");
ref.orderByChild("client/clientId").equalTo(userId).once("value", (snapshot) => {
upcomingOrders= snapshot.val()
console.log(upcomingOrders)
});
console.log(upcomingOrders) // Empty object, should contain snapshot.val()
}
It seems like it's updating by value. How can I update it by reference?
The data is loaded from Firebase asynchronously. While the data is being loaded, the function continues to run. When your console.log() just before the end of the function executes, the data hasn't been loaded yet. Then when the data becomes available, your callback function is invoked.
So: your upcomingOrders variable is being set correctly, just not at the moment where you currently want it.
To make this code work, you must move all code that needs the data into the callback function. So if you want to update the HTML, you do that from inside the callback:
onFetchMyUpcomingOrders(){
var upcomingOrders = {}
const userId = this.authService.getActiveUser().uid;
var ref = firebase.database().ref("orders/confirmed");
ref.orderByChild("client/clientId").equalTo(userId).once("value", (snapshot) => {
upcomingOrders= snapshot.val()
// TODO: update HTML and do whatever else needs the data
});
}
This is an extremely common pattern when using modern web APIs. Data is loaded asynchronously all the time, and callback such as this (or a more modern equivalent such as Promise or async/await) are the only way to deal with it.
I'm trying to store all returned objects into one object for later use. I have the loop set in place but when I try to execute some code with the new object, its empty. In this case here alert the object. I guess the alert is executing before the loop is complete. Any way to fix this?
var followers = new Array;
Parse.initialize("xxxxxx", "xxxxx");
var currentUser = Parse.User.current();
var users = JSON.stringify(currentUser);
var user = eval("(" + users + ")");
var listsfollow = user.Follow;
for (var i = 0; i < listsfollow.length; i++) {
var allUsers = Parse.Object.extend("User");
var query = new Parse.Query(allUsers);
query.get(listsfollow[i], {
success: function (results) {
followers.push(results);
},
error: function (object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
};
alert(followers);
You are correct and the AJAX stuff in jquery is async, and so the .get() will happen after the alert. The quickest and dirtiest way would be to set it to an async to false before issue the request: http://api.jquery.com/jQuery.ajax/
A better way would be to have your code respond to the result of the call whenever it is ready. This will prevent page blocking and make your code faster.
This is an asynchronous query, and the alert is being shown right after the query has initiated, so it's unlikely that it will be executed after the query has returned any results.
Any code that depends on the query's results should be moved into either the success or error callback functions.
I couldn't figure out how to make it wait for the query but I used this to do something after the last loop iteration. Worked great.
if((--remaining)==0)alert(followers);
I have the following function:
function loginStudent() {
var advisorKEY = "<dtml-var expr="py_get_alias()">";
var studentKEY = "<dtml-var SID>";
var URL = "py_logging_sessionOpen?AdvisorKEY=" + advisorKEY + "&StudentKEY=" + studentKEY;
key = "";
$j.get(URL, function(data) {
key = data;
});
alert(key);
}
The py_loggin_sessionOpen is just a python script running on my server.
It returns a single string. I need the response of that script to determine the next action. The script returns the value perfectly, and I can easily check the value by putting an alert within the function(data) in get.
My main question is: how to get the key value to be changed outside the scope of function(data)?
I assumed because I defined it externally it would act as a global variable.
Moving it outside loginStudent() does not solve the problem either.
Any ideas?
$j.get() is going to be an asynchronous call. That means it fires, and the rest of the execution continues. Anything that relies on that call needs to be done in the callback, like so:
$j.get(URL, function(data) {
key = data;
alert(key);
} );
If everything else is good, you'll see the value you expect.
The problem with your code is that $j.get executes asynchronously. That's the reason you pass a callback to it.
If you wish to write asynchronous code synchronously then you should read this answer: https://stackoverflow.com/a/14809354/783743
Edit: It seems that you have created a global variable called key by not declaring it with var. Hence it should be visible in other functions as long as they are called after the callback.
Would you care to provide us these other functions?
I am attempting to create an array of objects and then access object properties within the array, but it comes back undefined. I call the createObjArray() function and immediately after I do a console.log(objArray[1]); and it prints out the object with all it...s properties just fine. However, if I attempt to do console.log(objArray[1].name); firebug prints "undefined". Also, when stepping through my code in firebug I can mouse over objArray[1].name and it displays the correct name. What is happening here, it's driving me nuts.
var objArray = [];
function createObjectArray(numOfObjs) {
for(var i=0; i<numOfObjs; i++) {
packages.push(initObj(i));
}
}
function initObj(i){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
});
return p;
}
This will work:
var objArray = [];
function createObjectArray(numOfObjs, callback) {
var filledPackage = [];
var nbLeft = numOfObjs;
for(var i=0; i<numOfObjs; i++) {
initObj(i, function(p){
filledPackage.push(p);
nbLeft--;
if (nbLeft === 0){
callback(filledPackage);
}
});
}
}
function initObj(i, callback){
var newPackage;
var p = {};
$.getJSON('.../package' + i + '.json', function(data) {
newPackage = new Package(data);
p.name = newPackage.name;
p.id = i;
callback(p);
});
}
//Get a filled object array:
createObjectArray(5, function(filledArray){
objArray = filledArray;
//Code here will be executed AFTER all the $.getJSON queries have returned.
//objArray is not empty.
});
//Code here will be executed WHILE the getJSON queries are running and
//while objArray is still empty. Due to the way the JS event loop works,
//it is impossible that code placed here will be able to use the content
//of objArray unless you call an async function such as anything AJAX or
//setTimeout, but that's iffy. Code you want to be executed once objArray
//has been filled should be inside of the callback above.
The problem is that $.getJSON is aynchronous, meaning that it doesn't automatically returns a result. Instead, you give it a callback. A callback is a function to execute once it has received a result. In this case, the callback is the anonymous function created when calling $.getJSON. That callback receives the result from the server, adds it to the array and then checks if the array has been filled. Since we're doing async code due to the $.getJSON function, we must return the result asynchronously too. To do so, we demand the initObj function to receive a function to call once it has completed (another callback). We call that callback and pass it the parameter. We then return the filled array through a callback once again.
Your call to $.getJSON is asynchronous. When initObj() returns p it is still an empty object.
However initObj() creates a closure which captures a reference to p so when $.getJSON returns p is populated.
This is why the object seems empty in code you run immediately after populating the array. However by the time you run your console command the asynchronous calls have returned and the objects are populated.
You need to wait for all your async calls to return before continuing work on the array. One way to do this would be to increment a counter when you make a call and decrement it when a call returns, then when the final call returns the counter would drop to zero and you continue processing.
Alternatively you could setup a setTimout loop to keep polling the array the check when all its items are populated.
Both approaches are risky if you think one of the calls might fail, but the approach itself is fundamentally risky as you are making multiple ajax calls so you have to handle multiple possible failures. It would be a lot cleaner to grab all the data in one go so you can handle success / error states once in the success / error handler in jQuery.ajax.