I am new to firebase and trying to use the $asObject as in the angulerFire doc. Basically, I have profile as follows below. I use $asObject to update the email. However when I use $save(), it replaces the entire profile with only the email, rather than pushing it to the end of list ie it works like set() rather than push(). Is how it is meant to work? how do I only push?
Object
{profiles:
{peterpan:
{name:"Peter Trudy", dob:"7th March"}
}
}
My click function:
$scope.angularObject = function(){
var syncProfile = $firebase(ref.child("profiles").child("peterpan"));
var profileObject = syncProfile.$asObject();
profileObject.email= "peter#peterpan.com";
profileObject.$save();
};
You're looking for $update:
syncProfile.$update({ "email": "peter#peterpan.com" });
Note that $update is only available on $firebase and not on the FirebaseObject that you get back from $asObject. The reason for this is that $asObject is really meant as an object that is bound directly to an angular scope. You're not expected to perform updates to it in your own code.
By the way: if the rest of your code is using AngularFire in a similar way, you might consider dropping AngularFire and using Firebase's JavaScript SDK directly. It is much simpler to use, since it doesn't need to mediate between Firebase and Angular's way of working.
Related
I am using the firebase-tools shell CLI to test Firestore cloud functions.
My functions respond to the onCreate trigger for all documents in a certain collection, by using a wildcard, and then mutate that document with an update call.
firestore
.document(`myCollection/{documentId}`)
.onCreate(event => {
const ref = event.data.ref
return ref.update({ some: "mutation"})
})
In the shell I run something like this, (passing some fake auth data required by my database permissions):
myFunction({some: "data"}, { auth: { variable: { uid: "jj5BpbX2PxU7fQn87z10d4Ks6oA3" } } } )
Hoever this results in an error, because the update tries to mutate a document that is not in the database.
Error: no entity to update
In the documentation about unit testing it is explained how you would create mocks for event.data in order to execute the function without touching the actual database.
However I am trying to invoke a real function which should operate on the database. A mock would not make sense, otherwise this is nothing more then a unit test.
I'm wondering what the strategy should be for invoking a function like this?
By using an existing id of a document the function can execute successfully, but this seems cumbersome because you need look it up in the database for every test, and it might not be there anymore at some point.
I think it would be very helpful if the shell would somehow create a new document from the data you pass in, and run the trigger from that. Would this be possible maybe, or is there another way?
The Cloud Functions emulator can only emulate events that could happen within your project. It doesn't emulate the actual change to the database that would have triggered it.
As you're discovering, when your function depends on that actual change previously occurring, you can run into problems. The fact of the matter is that it's entirely possible that the created document may have already been deleted by the time you're handling the event in the function (imagine a user acts quickly to delete, but the event is delayed for whatever reason).
All that said, perhaps you want to use set() with SetOptions that indicate you want to merge instead of overwrite. Bear in mind that if the document was previously deleted (with good reason) before the event triggered, you'll unconditionally recreate the document, which may not be what the user wanted.
I have created a simple Pre Trigger in my CosmosDB collection.
function testTrigger() {
var context = getContext();
var request = context.getRequest();
var documentToCreate = request.getBody();
documentToCreate["test"] = "Added by trigger";
request.setBody(documentToCreate);
}
Though it seems, this trigger isn't fired at all.
What's also irritating, the getContext() call is marked with a green squiggly line in the script explorer.
The problem that the trigger doesn't seem to be... well... triggered, is that you aren't specifying it on the triggering operation. Triggers are not automatic. They must be specified for each database operation where you want them executed.
Personally, I think that was a unfortunate design choice and I've decided to not use triggers at all in my own code. Rather, I embed all the business rule constraints in stored procedures. I find this to be a bit cleaner and removes the worry that I'll forget to specify a trigger or (more likely) someone else writing code to the database won't even know that they should. I realize that someone can bypass my stored procedure and violate the business rules by going directly to the documents, but I don't know of any way in DocumentDB to protect against that. If there was a way to make triggers be automatic, that would have protected against that.
When using the REST API, you specify triggers in a header with x-ms-documentdb-pre-trigger-include or x-ms-documentdb-post-trigger-include. In the node.js SDK, you use the RequestOptions preTriggerInclude or preTriggerInclude property. For .NET, you specify it like this:
Document createdItem = await client.CreateDocumentAsync(collection.SelfLink, new Document { Id = "documentdb" },
new RequestOptions
{
PreTriggerInclude = new List<string> { "CapitalizeName" },
});
See comments for discussion on the green squiggly.
Simple question:
Will all obersvers automatically removed when I use goOffline (disconnect to firebase) ?
If not, is there another way to do it, because removeAllOberserves doesn't seem to work or must I keep an array of single handles?
UPDATE
I answer myself.
removeAllOberserves works well, if you call it with the reference you used to set the observer!
Example:
Firebase *userThreadRef;
userThreadRef = [userRef appendPathComponent: ThreadsPath];
[userThreadRef observeEventType: FEventTypeChildAdded withBlock: ^(FDataSnapshot *snapshot) {
...
}];
....
[userThreadRef removeAllObservers];
Do not use a new reference like this:
Firebase *newUserThreadRef = [userRef appendPathComponent: ThreadsPath];
[newUserThreadRef removeAllObservers];
Will all observers automatically removed when I use goOffline (disconnect to firebase) ?
No. Calling goOffline() will not automatically remove observers/listeners.
is there another way to do it, because removeAllOberserves doesn't seem to work or must I keep an array of single handles?
It's hard to say without seeing your code, but likely your expectations are just wrong.
You'll need to call removeAllObservers() on each reference. The All in the method name is for the fact that it removes the observers for all event types, not for all references.
I am confuse about why we have to use angularFireCollection built-in methods such as add() update() remove() etc instead of using firebase js api directly.
Since we just want to bind the firebase node with angularJS. We can just use
angularFireCollection(nodeRef) then if we want to do something with the binding node.
we can just go back to firebase js api and do something with the node. Example
nodeRef.push() .update() .remove().
To me, this is better than using angularFireCollection().methods(). Because
angularCollection is not completed compare to firebase js api
I don't have to learn / know AGAIN how ngFire works since I alredy learnt in firebase
different methods name confusing me. angularFireCollection().add() vs new Firebase().push()
The answer is that you do not have to use angularFireCollection at all. You can utilize Firebase methods directly. You can also use angularFire or angularFireCollection to sync data down to your client and then push data using Firebase methods directly.
angularFire and angularFireCollection are not a replacement for Firebase, but rather are wrappers intended to abstract away some of the complexities of syncing data to Angular bindings. To get an understanding of what those complexities are, you can simply look at the angularFireCollection code, and see the number of cases that have to be handled.
Consider the following example:
<h1>Hello {{user.name}}</h1>
<input ng-model="user.name" ng-change="change()" />
Without angularFire:
function($scope) {
$scope.user = {};
$scope.change = function() {
new Firebase('URL/users/kato/name').set($scope.user.name);
};
new firebase('URL/users/kato').on('value', function(snap) {
$scope.user = snap.val()||{};
});
}
With angularFire:
function($scope, angularFire) {
angularFire(new Firebase('URL/users/kato'), $scope, 'user');
}
Or, something more sophisticated:
<ul>
<li ng-repeat="user in users">
{{user.name}}
<input ng-model="user.name" ng-change="change(user)" />
</li>
</ul>
Without angularFireCollection, this would be 100+ lines of code (i.e. about the length of the angularFireCollection object) as it needs to handle child_added, child_removed, child_changed, and child_moved events, adding them into an ordered array for Angular's use, looking up keys on each change and modifying or splicing, maybe even moving records in the array. And it also needs to add changes on the client and sync them back to Firebase by finding the correct key for each record.
With angularFireCollection, it can be done in a few lines, like so:
function($scope, angularFireCollection) {
$scope.users = angularFireCollection(new Firebase('URL/users/kato'));
$scope.change = function(user) {
$scope.users.update(user);
};
}
Of course, these abstractions don't cover every use case and there are certainly plenty of places that using Firebase directly is both practical and appropriate.
Hope that helps.
For security reasons, I want to add and remove properties of documents before publishing them to the client, depending on some dynamic calculations. I follow the Meteor documentation and this other SO question.
For example simplicity, say I try to add the following static property to every document (SERVER SIDE ONLY):
var Docs = new Meteor.Collection('docs', {
transform: function (f) {
console.log('Tagging doc: ' + f._id);
f.myProp = 1;
return f;
}
});
For some strange reason, this does not work:
Only some documents trigger the transform function, not all (I can see this through the console logging)
On the client side, none of the documents are tagged with myProp
I haven't tried to put the transform on both the client and the server, because in my real life app I cannot do the necessary computation on the client.
Transform functions on collections are intended for convenience, not security -- note that when you call observeChanges on a cursor, the information is not passed through the transform function (it is passed through the transform when you call observe). The default way of publishing a cursor works by calling observeChanges on it.
If you want to strip off fields of a cursor you're publishing, use the fields option to find on your collection. If you want to do something more complicated, you can explicitly do whatever computation you need if your publish function calls added, changed, and removed itself, instead of returning a cursor. Check out the docs for Meteor.publish for details.