5

When listening on the "child_added" event with:

ref.on("child_added", function (snapshot) {

});

This callback function will initially run once for every child that exists in the reference.

This event will be triggered once for each initial child at this location, and it will be triggered again every time a new child is added.

https://firebase.google.com/docs/reference/node/firebase.database.Reference

I want to take advantage of this fact along with the ordering functions in order to construct an ordered array:

orderedArray = [];

ref.orderByValue("rating").on("child_added", function (snapshot) {
    orderedArray.push(snapshot.val())
});

// how do I run a callback after the last child has been added?

However, there is no way (to my knowledge) to tell the last time the child_added callback has been called, thus I can't accurately run my own callback after the the last child has been added to my array.


Here's my workaround to this right now:

orderedArray = [];

ref.orderByValue("rating").on("child_added", function (snapshot) {
    orderedArray.push(snapshot.val())
});

setTimeout(function() {

    ref.off("child_added") // unbind event
    callback()

}, 3000)

This is pretty sketchy, especially in the case that it'll take more than 3 seconds to get all the data from the database.

Any ideas here?

octopod
  • 824
  • 2
  • 10
  • 23

2 Answers2

13

You can iterate over parent snapshot and order the children into an array using DataSnapshot.forEach:

const ref = firebase.database().ref().child('items');
const items = [];
ref.once('value', snap => {
  snap.forEach(item => { items.push(item) });
  console.log(items);
});

Since you're calling ref.off() to read the data once, it makes sense that to use the .once() method and iterate of the parent snapshot instead.

David East
  • 31,526
  • 6
  • 67
  • 82
  • and `forEach()` will read them in order even if I used an ordering function like `orderByValue()`? – octopod Jun 15 '16 at 00:35
  • What about cases when there is a large number of items in snap? It sounds like this solution first loads all items in memory then gives them to the callback. – Ivan Aug 02 '17 at 14:39
  • And how do you make it so that when a new state is added it is saved in the array? that's the intention I see when using `child_added` or this method is only for the report? – Hernan Humaña Dec 04 '18 at 18:09
0

What I try to do is use a observeSingleEvent listener.

// Following a Swift code but the logic remains same.
Database.database()
.reference(withPath: "The_Path")
.observeSingleEvent(of: .value) { (snapshot) in
    // Iterate and save the values from the snapshot.
    // Now initiate `childAdded` and `childChanged` listeners.
    self.keepObserving()
}

And on completion add childAdded and childChanged.

func keepObserving() {
    Database.database()
    .reference(withPath: "The_Path")
    .observe(.childAdded) { (snapshot) in
        // Check if the value in the snapshot exists in the your array or data model.
        // If not then add it to your container else return.
    }

    Database.database()
    .reference(withPath: "The_Path")
    .observe(.childChanged) { (snapshot) in
        // Find the indexOf the data in snapshot in array or container.
        // If the index is found or exist replace the changed value.
    }
}
Lazy
  • 670
  • 5
  • 14