I am new to firebase and have read through the docs, but can't seem to find a away to get a list of all Ladders with populated User data. I don't really want to duplicate the user data in every ladder they are a member of.
Here is my data structure:
{
"ladders" : [ {
"description" : "Real Real Tennis",
"name" : "Ping Pong",
"players" : {
"simplelogin:5" : true
}
}, {
"description" : "Real Tennis",
"name" : "Mario Tennis",
"players" : {
"simplelogin:5" : true
}
} ],
"users" : {
"simplelogin:5" : {
"email" : "[email protected]",
"md5hash" : "1e737a7b6e1f0afb1d6fef521097400b",
"name" : "Bob",
"username" : "bob1"
}
}
}
Here is my best attempt, but it looks like the ladders lookup finishes and returns before the users lookup is done so it returns with players being empty.
laddersRef.on('value', function(laddersSnapShot){
var ladders = []
laddersSnapShot.forEach(function(ladderData) {
var ladder = ladderData.val();
ladder.players = [];
ladder.id = ladderData.key();
laddersRef.child(ladder.id).child('players').on('value', function(snap){
snap.forEach(function(player){
usersRef.child(player.key()).on('value', function(snap1){
ladder.players.push(snap1.val())
})
})
})
ladders.push(ladder)
});
});
Most everything about this example is broken. A read through the guide is essential before this question can be answered in a meaningful way. As written, your code establishes a new listener on each ladder's list of players any time any field anywhere in ladders/ is modified.
This occurs because laddersRef.on('value') will be updated any time there is a change anywhere in that path. Each time this occurs, you then establish laddersRef.child(ladder.id).child('players').on('value'), which creates multiple listeners on each ladder's players/ path. The fundamentals of this are covered in retrieving data
The correct answer here is to utilize the tools as intended, rather than trying to make a slow, tedious, error-prone and synchronous process out of asynchronous API methods.
To directly answer your question, this is what you asked for:
function done(ladders) {
console.log(ladders);
}
laddersRef.on('value', function(laddersSnapShot){
function doneWithLadder() {
if( ++laddersDone === laddersNeeded ) {
done(ladders);
}
}
var ladders = [];
var laddersDone = 0;
var laddersNeeded = laddersSnapShot.numChildren();
laddersSnapShot.forEach(function(ladderData) {
var ladder = ladderData.val();
var playersFound = 0;
var playersNeeded = ladderData.numChildren();
ladder.players = [];
ladder.id = ladderData.key();
laddersSnapshot.child('players').forEach(function(ps) {
usersRef.child(ps.key()).once('value', function(userSnap) {
ladder.players.push(usersSnap.val());
if( ++playersFound === playersNeeded ) {
doneWithLadder();
}
});
});
});
});
But this is more elegant (and much more elegant would be to use a framework and avoid this entirely):
function addLadder(snap) {
putLadderInDom(snap.key(), snap.val());
snap.ref().child('players').on('value', changePlayers);
// Use whatever framework you have available to put this into
// the DOM. Be sure to keep the record key around so you can
// add the user in later
}
function changePlayers(playersSnap) {
var ladderKey = playersSnap.parent().key();
var numFetched = 0;
var numPlayersToFetch = playersSnap.numChildren();
var profiles = {};
playersSnap.forEach(function(ss) {
loadProfile(ss.key(), function(userSnap) {
profiles[userSnap.key()] = userSnap.val();
if( ++numFetched === numPlayersToFetch ) {
updatePlayers(ladderKey, profiles);
}
});
});
}
function updatePlayers(ladderKey, profiles) {
// use whatever framework you have available to insert profiles
// into the DOM
}
function loadProfile(key, callback) {
usersRef.child(key).once('value', callback);
}
laddersRef.on('child_added', addLadder);