Firebase: How to use child changed with tables? - javascript

I am not sure how child_changed can be used with tables. child_added will work in populating the table but when some change in data occurs, then how to identify which row needs to be updated?
Official documentation suggests child_added to be used with child_changed and child_removed but I'm not sure how the latter 2 will work.
The child_changed event is triggered any time a child node is modified. This includes any modifications to descendants of the child node. It is typically used in conjunction with the child_added and child_removed events to respond to changes to a list of items. The snapshot passed to the event listener contains the updated data for the child.
All I can think of is storing an ID along with the row data, then listening for changes, then getting the ID stored and changing the row data accordingly.

If you are like me then you might use data ID's as keys in your data. EG:
{
"customers":
{
"62562":
{
"name": "Joe blogs"
}
}
}
firebase.database().ref('/customers/62562').on('value', function(snapshot){
console.log(snapshot.val());
});
The above code gives you changes to the data contained in '/customers/62562' but you you won't know where it is from. You need to look at the reference of the snapshot if you wish to know the customerId like:
var customerId = snapShot.ref.key returns '62562'
You can even go further up the branch and find out which branch holds this customer
var parentName = snapShot.ref.parent.key returns 'customer'

Related

How to only read changes in firebase realtime database

I have a database collection with readings, each new reading needs to be checked if it's out of the ordinary, if it is, there needs to be an alert sent.
So i'm using db.ref('collection').on('child_added', (child => { check(child); });
The problem with the .on function is that when the listener is added, all previous data is also read.
So how do i read a collection that only reads the changes in the database, also when the listener is first added? Or if that doesn't work, how do I differentiate the already added data with the new data?
The Firebase database synchronizes the state of whatever query or reference you attach your listener to. There is no option to only get new nodes built into the API.
If you want only new nodes, you will have to:
Ensure each node has an associated timestamp or order. If you're using Firebase's built-in push() keys, those might already serve that function.
Know what "new" means to the client, for example by either keeping the last timestamp or push key that it saw.
And then use a query to only request nodes after the stores timestamp/key.
So for example, if you only want to read nodes that are created after the moment you attach the listener, you could do something like this:
let now = db.ref('collection').push().key; // determine current key
db.ref('collection').orderByKey().startAt(now).on('child_added', ...)

Does firebase push method with empty values, just for getting the ID, trigger the child_added event?

Reading firebase real time database docs it is not clear when the child_added event triggers exactly. It says it triggers when a new child is added to a node, so far so good. The docs also say that, if you want to get the next available unique Id on a path, you just call push() on that path to get a reference that will have a unique ID. However, it is not clear if this empty push call will be considered an event of child_added or if it will be ignored. Once you get the ID, you can not push again or you will get another ID (that is just my guess) so you just set the given reference with the data you want it to contain. It is not clear either if this last operation will trigger a child_added event.
Let me ilustrate with a bit of code with inline questions:
const dbRef = db.child('todos')
const newTodoRef = dbRef.push() // does this trigger child_added event?
newTodoRef.set({ id: newTodoRef.key, name: 'test' }) // and does this?
Calling push() without any arguments does not write any data, so it does not trigger any events yet. It merely creates a reference in the code to a new unique location.
Calling set(...) on this reference does then write data, so does trigger events.

How to get the key as soon as value gets changed in firebase?

I have my firebase structure like this
I want to get a the key/number as soon as status becomes 1.
I tried..
//Listen for number
var ref = firebase.database().ref("games/" + gameId + "/numbers");
ref.orderByChild('status').equalTo('1').on("value", function(snapshot)
{
snapshot.forEach((function(child){
console.log(child.key);
}));
});
But every time status gets updated i am getting every number with the status = 1.
I want just the last updated number.
use child_changed instead of value. It will only return single value in which child will be changed so we will get key directly by snapshot.key.
//Listen for number
var ref = firebase.database().ref("games/" + gameId + "/numbers");
ref.orderByChild('status').equalTo('1').on("child_changed", function(snapshot)
{
console.log(snapshot.key);
});
I think, it will be helpful for you.
When you listen for the value event, you get a snapshot with all matching data each time.
If you want to get more granular notifications, you can listen for child_* events. For example to hear when a node status gets set to one, you can listen for child_added:
ref.orderByChild('status').equalTo('1').on("child_added", function(snapshot)
{
console.log(snapshot.key);
});
You'll note that I also remove the forEach. Since child_ events fire for each individual child, you no longer need the loop.
If you find it hard to figure out which child_ event to listen to, I find it easiest to think of the query as a "view" on the data. When a child node's status is set to 1, it enters the view, so it is child_added. When a child node's status is set to another value, it leaves the view, so it'd fire child_removed.

Retrieving Objects from Firebase using Value() Method

I cant figure out how to get "value" to read the data in my FB object the same way that the "child_added" does.
The following code:
postNotes.on("value", function(snapshot){
console.log("VALUE FUNCTION REACHED")
var note = snapshot.val();
console.log(note.noteObject);
});
postNotes.on("child_added", function(snapshot){
console.log("CHILDADDED FUNCTION REACHED");
var note = snapshot.val();
console.log(note.noteObject);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
...displays this in the console:
And here is the data in the FB console:
I don't use any libraries. I would simply like the data from FB to display when my page loads, and continue to update each time new data is pushed to the FBDB.
Am I missing the point of the "value" parameter? What can I do better?
Please forego the answers that involve things such as "read the docs", "use jquery", or otherwise useless answers some StackO users seem to be so fond of.
I've been digging around this problem of mine for a few hours and based on what else I see in StackO a few others have had a similar issue. Thank you so much for taking a look at my question and lending any insight you may have.
You use the same postsRef to listen to both value and child_added events. This is not a common use patterns.
The value event fires when the value of the node changes. So whenever the parent node of -J....NI3 changes, the entire parent node is passed into your callback as the snapshot. If you check the data in the Firebase dashboard, you'll see that this parent node does not have a noteObject child.
Compare that to the child_added event, which fires when a child node is added to the node on which you listen. The callback to child_added in that case gets the new child node passed as its snapshot. And the new child node indeed has a noteObject child.
You'll normally listen to value events on single, lowest-level nodes. When you have a collection of nodes (like you do here), you'll usually use child_added (and its _changed, _deleted and _moved brethren). But just in case you really want to use on('value' with a collection:
postNotes.on('value', function(notesSnapshot) {
notesSnapshot.forEach(function(noteSnapshot) {
console.log(noteSnapshot.val());
});
});
Just be aware that such an approach is less efficient than listening to the various child_ events, especially as you dynamically add or change children in the collection.

data-win-bind issues: converter only runs once and unable to bind id of element

I have the following html that is bound to an object containing id and status. I want to translate status values into a specific color (hence the converter function convertStatus). I can see the converter work on the first binding, but if I change status in the binding list I do not see any UI update nor do I see convertStatus being subsequently called. My other issue is trying to bind the id property of the first span does not seem to work as expected (perhaps it is not possible to set this value via binding...)
HTML:
<span data-win-bind="id: id">person</span>
<span data-win-bind="textContent: status converter.convertStatus"></span>
Javascript (I have tried using to modify the status value):
// persons === WinJS.Binding.List
// updateStatus is a function that is called as a result of status changing in the system
function updateStatus(data) {
persons.forEach(function(value, index, array) {
if(value.id === data.id) {
value.status = data.status;
persons.notifyMutated(index);
}
}, this);
}
I have seen notifyMutated(index) work for values that are not using a converter.
Updating with github project
Public repo for sample (not-working) - this is a really basic app that has a listview with a set of default data and a function that is executed when the item is clicked. The function attempts to randomize one of the bound fields of the item and call notifyMutated(...) on the list to trigger a visual updated. Even with defining the WinJS.Binding.List({ binding: true }); I do not see updates unless I force it via notifyReload(), which produces a reload-flicker on the listview element.
To answer your two questions:
1) Why can't I set id through binding?
This is deliberately prevented. The WinJS binding system uses the ID to track the element that it's binding to (to avoid leaking DOM elements through dangling bindings). As such, it has to be able to control the id for bound templates.
2) Why isn't the converter firing more than once?
The Binding.List will tell the listview about changes in the contents of the list (items added, removed, or moved around) but it's the responsibility of the individual items to notify the listview about changes in their contents.
You need to have a data object that's bindable. There are a couple of options:
Call WinJS.Binding.as on the elements as you add them to the collection
Turn on binding mode on the Binding.List
The latter is probably easier. Basically, when you create your Binding.List, do this:
var list = new WinJS.Binding.List({binding: true});
That way the List will call binding.as on everything in the list, and things should start updating.
I've found that if I doing the following, I will see updates to the UI post-binding:
var list = new WinJS.Binding.List({binding: true});
var item = WinJS.Binding.as({
firstName: "Billy",
lastName: "Bob"
});
list.push(item);
Later in the application, you can change some values like so:
item.firstName = "Bobby";
item.lastName = "Joe";
...and you will see the changes in the UI
Here's a link on MSDN for more information:
MSDN - WinJS.Binding.as
Regarding setting the value of id.
I found that I was able to set the value of the name attribute, for a <button>.
I had been trying to set id, but that wouldn't work.
HTH
optimizeBindingReferences property
Determines whether or not binding should automatically set the ID of an element. This property should be set to true in apps that use Windows Library for JavaScript (WinJS) binding.
WinJS.Binding.optimizeBindingReferences = true;
source: http://msdn.microsoft.com/en-us/library/windows/apps/jj215606.aspx

Categories