AngularFire binding to scope vs $bindTo? - firebase

In angular fire documentation, here are the two line I am trying to understand:
// To make the data available in the DOM, assign it to $scope
$scope.data = obj;
// For three-way data bindings, bind it to the scope instead
obj.$bindTo($scope, "data");
Are they doing the same thing? Do both bind to scope for 3 way data binding?

Nope, they're not doing the same thing. That would be a pretty silly API. :-)
Running $scope.data = $firebaseObject(ref) binds the data in the Firebase location to the scope. Any update to the data in the Firebase database, will automatically be reflected in the scope and thus - if you bind an HTML element to $scope.data - also be updated on screen. The flow is uni-directional: from the database, to the screen.
Calling obj.$bindTo($scope, "data") both binds the data in the Firebase location to the scope and listens to changes to $scope.data that it then send back to the Firebase database automatically. So this creates a bi-directional, three-way binding. Updates in the HTML are sent to the database, updates to the database are sent to the screen.
This is all pretty well documented, for example in this blog post "Three-Way Data Binding with Firebase and Angular". The regular AngularFire documentation also contains a good section about three-way data binding.

Related

Getting data from Flutter Firebase Realtime Database when onChildAdded

I will first say that I am a firmware developer trying to make a web interface for a personal project. My question might be basic as this is my first web interface with a database. I have searched quite a bit on how to achieve what I am trying to do without success, so I suspect I am not approaching this the right way, but here it is:
Basically I have a device pushing data to a firebase realtime database :
the push function generates an unique Id for me automatically which is nice...
Now In my flutter interface, I want to display the latest entry on a widget so I am using the onChildAdded functionnality of flutterfire:
ref.onChildAdded.listen((DatabaseEvent event) {
print(event.snapshot.key);
print(event.snapshot.value);
});
My first issue is that this function is triggered for all the child at first before waiting for new ones which is unnecessary and could be a problem when I begin to have a lot of data. Is there a way to simply get the latest sample and still get an event when one is added? (I still want to keep the old data to be able to make charts with them)
My second problem is the format of the received data:
{
-Mx2PCeptLf2REP1YFH0: {
"picture_url": "",
"time_stamp": "2022-02-28 21:56:58.502005",
"temperature": 27.71,
"relative_humidity": 42.77,
"eco2": 691,
"tvoc": 198,
"luminosity": 4193,
"vpd": 1.71
}
}
before using the put method, I didn't have the automatically generated ID so I was able to cast this as a Map<String, dynamic> and use the data like this in my widget:
Text(snapshot.data!["temperature"].toString())
but now the added nesting with the generated id is giving me a headache as I can't seem to figure out how to simply get the data.
So if anyone could help me to always get the single latest data when subscribing to a collection and how to access that data within my State of my StatefulWidget that would be much appreciated!
My first issue is that this function is triggered for all the child at first before waiting for new ones which is unnecessary and could be a problem when I begin to have a lot of data.
The Firebase Realtime Database synchronizes the state of the path/query that you listen to. So it doesn't just fire an event on onChildAdded for new data, but also for any existing data that you request. There is (intentionally) no way to change that behavior.
So your options here are limited:
You can remove the data that your application has processed, so that it doesn't get passed to the clients again. That means you essentially implement a message-passing mechanism on top of Firebase's data-synchronization semantics.
You can use a query to only request a certain subset of the child nodes at a path. For example, if you remember the latest key that you've received, you can get data from there on with ref.orderByKey().startAt("-Mx2LastKeyYouAlreadySaw").
Since this comes up regularly, I recommend also checking:
How can I setup Firebase to observe only new data?
How to get only updated data Firebase
How to fetch only latest specific data from Firebase?
and more from these search results
My second problem is the format of the received data:
The screenshot of your database shows two keys, each of which has a single string value. The contents of that value may be JSON, but the way they are stored is just a single string.
If you want to get a single property from the JSON, you either have to:
Decode the string back into JSON with jsonDecode.
Fix the problem at the source by storing the data as proper JSON, rather than as a string.

Meteor Client-side Collection Document Appears and Disappears

After lots of reading, I'm starting to get a better handle on Meteor's publish/subscribe model. I've removed the autopublish training wheels from my first app and while I have most everything working, I am seeing one issue.
When the app first loads, my publish and subscribe hooks work great. I have a block of code that runs in a Tracker.autorun() block which makes the subscribe calls, I am able to sequentially wait for data from the server using ready() on my subscribe handles, etc.
One feature of my app is that it allows the user to insert new documents into a collection. More specifically, when the user performs a certain action, this triggers an insert. At that point, the client-side JS runs and the insert into MiniMongo completes. The reactive autorun block runs and the client can see the inserted documented. The client updates the DOM with the new inserted data and all is well.
Furthermore, when I peek into the server-side MongoDB, I see the inserted document which means the server-side JS is running fine as well.
Here's where it gets weird. The client-side autorun block runs a second time (I'm not sure why) and this time, the client no longer has the inserted item. When the DOM renders, the newly inserted item is now gone. If I reload the page, all is well again.
Has anyone seen this behavior before? I'm also noticing that the server-side publish call runs once on page load but then it doesn't run again after the insert. This seems wrong because how else will the client get the reconciled data from the server after the insertion (i.e. after Meteor's client-side latency compensation)?
The important functions (ComponentInstances is the collection that is bugging out):
Publish block:
Meteor.publish('allComponentInstances', function (documentId, screenIndex) {
console.log(`documentId: ${documentId} screenIndex: ${screenIndex}`)
const screens = Screens.find({ownerDocumentId: documentId})
const selectedScreen = screens.fetch()[screenIndex]
return ComponentInstances.find({_id: {$in: selectedScreen.allComponentInstanceIds}})
})
Subscription block in autorun:
// ... a bunch of irrelevant code above
const allComponentInstancesHandle = Meteor.subscribe('allComponentInstances', document._id, 0)
if (allComponentInstancesHandle.ready()) {
isReady = true
screens = Screens.find({ownerDocumentId: document._id}).fetch()
const componentInstanceObjects = ComponentInstances.find().fetch()
allComponentInstances = {}
componentInstanceObjects.map((componentInstance) => {
allComponentInstances[componentInstance._id] = componentInstance
})
}
This is most probably you're inserting documents from client side. And you have not set up your permission rules properly. When you remove autopublish and insecure from your app, you are not allowed to insert/update/remove documents into collection unless you have allow/deny rules set up in the server side.
Meteor has a great feature called latency compensation which tries emulate your db operations before it gets the actual write operation in the db. And when the server tries to write in the db, it looks for allow/deny rules.If the permission rules doesn't allow the db operation or Whatever the reason( either allow/deny or authentication) for not actually written in the db, then the server data gets synchronized with your client side db.
This is why i assume you are seeing your document being inserted for the first time and gets disappeared within a second.
check this section of meteor docs.
http://docs.meteor.com/#/full/allow
I ended up solving this a different way. The core issue, I believe, has nothing to do with accept/deny rules. In fact, their role is still hazy to me.
I realize now what I've been reading all along in the Meteor docs: the publish functions return cursors. If the cursor itself doesn't change (e.g. if you're passing specific keys you want to fetch), then it won't really work as a reactive data source in the sense that new documents in a collection will not make the data publish again. You are, after all, still requesting the same keys.
The way forward is to come up with a publish cursor that accurately reflects the reactive data you want to retrieve. This sounds abstract but in practice, it means make sure the cursor is general, not specific to the specific keys you are retrieving.

Angular-like client side data binding and reactivity with Meteor?

I'm trying to wrap my head around Meteor's way of dealing with reactivity and I want to make sure I've got some concepts right.
Take the follow reactivity example:
A user types something into a form field. The thing that he is typing is instantly displayed somewhere else on the page, as the user is typing, letter by letter. An instantaneous duplication.
From what I know about Angular, this is a very common example of reactivity. Angular binds the input directly to the output on the client side. There's nothing in between.
Correct me since I could be wrong, but Meteor can do this, but the input would first need to be captured and stored into a Mongo + MiniMongo DB (perhaps only as a collection in local storage), there would need to be a subscribe step, and those values would then be read and displayed on the page.
Is there a way to directly bind an event on the front end to another thing on the front end like Angular does?
Is this right? For Meteor to have the front-end-only reactivity of Angular it must first go through the intermediary of a collection, meaning extra code would be necessary to accomplish this compared to Angular?
The example in the Meteor Docs:
Deps.autorun(function () {
Meteor.subscribe("messages", Session.get("currentRoomId"));
});
So here, when the data of currentRoomId changes, the function is reactive to that data change and the function runs (in this case Meteor subscribes to messages).
Using Session variables is the only way I see of possibly binding two parts of a view together directly. Are there other ways?
Meteor's client-side reactivity system (Deps) is not coupled with its live MongoDB syncing. You can use it with any reactive data source which implements the right interface, including data sources which are entirely client-side. For example, you can use the built-in Session object. This is just a client-side key-value store with support for Meteor's reactivity, and you don't have to do any publish or subscribe to use it.
This standard way to do this sort of thing looks something like this:
<input id="field" value="{{fieldValue}}">
Template.form.fieldValue = function () {
return Session.get("fieldValue");
};
Template.form.events({
"input #field": function (evt) {
Session.set("fieldValue", $(evt.currentTarget).val());
}
});
Now the Session variable fieldValue is synced up to the form field. You can call Session.get("fieldValue") in some helper and that template will re-render when the user types in the form field. And if you call Session.set("fieldValue", "blah") then the form field will update itself.
As for your edit: You can make your own reactive data sources using Deps.Dependency, or you could meteor add reactive-dict although that's not documented. There may be packages on Atmosphere.

Firebase 3 way data binding not working as expected

I have an AngularJS service that returns a firebase ref.
.factory('sessionManager', ['$firebase','$scope', function($firebase){
var ref=new Firebase('https://telechat.firebaseio.com/sessions');
return $firebase(ref);
}])
In the controller, I have added the dependency and called $bind.
$scope.items=sessionManager;
$scope.items.$bind($scope,'sessions').then(function(unbind){
unbind();
});
But when I print it to the console, the returned data has a collection of functions like $add , $set ,.. etc in addition to the array of data.
Why is this occurring? Am I doing it the wrong way?
If I'm reading the question correctly, you may be under the impression that $bind() is a transformation of the $firebase object into a raw data array or object. In essence, the only difference between a $firebase instance and $bind is that data is local changes are automagically pushed back to Firebase from Angular. Whereas, when you use $firebase without $bind, you need to call $save to push local changes.
Keeping in mind that $firebase is a wrapper on the Firebase API and not a simple data array, you can still treat it like raw data in most cases.
To iterate data in a Firebase object, you can use ngRepeat:
<li ng-repeat="(key, item) in $sessions">{{item|json}}</li>
Or if you want to apply filters that depend on arrays:
<li ng-repeat="(key, item) in $sessions | orderByPriority | filter:searchText">
Or in a controller using $getIndex:
angular.forEach($scope.sessions.$getIndex(), function(key) {
console.log(key, $scope.sessions[key]);
});
The $add/$update/etc methods mentoined are part of the $firebase object's API. The documentation and the tutorial should be great primers for understanding this process.
This is also a part of the API that is continuing to evolve to better match the Angular way of doing things and feedback from users.

Update document in Meteor mini-mongo without updating server collections

In Meteor, I got a collection that the client subscribes to. In some cases, instead of publishing the documents that exists in the collection on the server, I want to send down some bogus data. Now that's fine using the this.added function in the publish.
My problem is that I want to treat the bogus doc as if it were a real document, specifically this gets troublesome when I want to update it. For the real docs I run a RealDocs.update but when doing that on the bogus doc it fails since there is no representation of it on the server (and I'd like to keep it that way).
A collection API that allowed me to pass something like local = true this would be fantastic but I have no idea how difficult that would be to implement and I'm not to fond of modifying the core code.
Right now I'm stuck at either creating a BogusDocs = new Meteor.Collection(null) but that makes populating the Collection more difficult since I have to either hard code fixtures in the client code or use a method to get the data from the server and I have to make sure I call BogusDocs.update instead of RealDocs.update as soon as I'm dealing with bogus data.
Maybe I could actually insert the data on the server and make sure it's removed later, but the data really has nothing to do with the server side collection so I'd rather avoid that.
Any thoughts on how to approach this problem?
After some further investigation (the evented mind site) it turns out that one can modify the local collection without making calls to the server. This is done by running the same methods as you usually would, but on MyCollection._collection instead of just on Collection. MyCollection.update() would thus become MyCollection._collection.update(). So, using a simple wrapper one can pass in the usual arguments to a update call to update the collection as usual (which will try to call the server which in turn will trigger your allow/deny rules) or we can add 'local' as the last argument to only perform the update in the client collection. Something like this should do it.
DocsUpdateWrapper = function() {
var lastIndex = arguments.length -1;
if (arguments[lastIndex] === 'local') {
Docs._collection.update(arguments.slice(0, lastIndex);
} else {
Docs.update(arguments)
}
}
(This could of course be extended to a DocsWrapper that allows for insertion and removals too.)(Didnt try this function yet but it should serve well as an example.)
The biggest benefit of this is imo that we can use the exact same calls to retrieve documents from the local collection, regardless of if they are local or living on the server too. By adding a simple boolean to the doc we can keep track of which documents are only local and which are not (An improved DocsWrapper could check for that bool so we could even omit passing the 'local' argument.) so we know how to update them.
There are some people working on local storage in the browser
https://github.com/awwx/meteor-browser-store
You might be able to adapt some of their ideas to provide "fake" documents.
I would use the transform feature on the collection to make an object that knows what to do with itself (on client). Give it the corruct update method (real/bogus), then call .update rather than a general one.
You can put the code from this.added into the transform process.
You can also set up a local minimongo collection. Insert on callback
#FoundAgents = new Meteor.Collection(null, Agent.transformData )
FoundAgents.remove({})
Meteor.call 'Get_agentsCloseToOffer', me, ping, (err, data) ->
if err
console.log JSON.stringify err,null,2
else
_.each data, (item) ->
FoundAgents.insert item
Maybe this interesting for you as well, I created two examples with native Meteor Local Collections at meteorpad. The first pad shows an example with plain reactive recordset: Sample_Publish_to_Local-Collection. The second will use the collection .observe method to listen to data: Collection.observe().

Resources