firebaseObject $save() replaces the content of reference - firebase

I can't make $save() work (i'm using firebaseObject)
var user = new Firebase("URL");
user.name = 'mark';
user.$save();
After this my user's all records are simply replaced by a single
name: mark

The data isn't loaded yet in your example. You'll have to wait by hooking into the firebase .on or (as it seems you're using angularfire) you can look into working with $loaded based on the reference.
var user = $firebaseObject(new Firebase("URL"));
user.$loaded().then(function(){
user.name = 'mark';
user.$save();
});

Actually, i missed the $firebaseObject in code sample above.
The problem was that my firebaseObject was not loaded yet. $loaded() fixed it. Here is the final working code:
var user = $firebaseObject(new Firebase("URL"));
user.$loaded().then(function(){
user.name = 'mark';
user.$save();
});

Related

Firebase "child_added" event

I want a function to be called whenever a new child is added to "chat". I know this can be done using "child_added" event. However, from that function, I want to modify the newly created child.
So suppose a new child "123456" is added to chat and I want to update the "123456" object in the DB. I think I could solve the problem if I somehow manage to get the key (in this case it's 123456) of the newly added object. Is there a way to achieve this?
That should do the trick:
ref.on('child_added', function(childSnapshot, prevChildKey) {
var key = childSnapshot.key;
...
});
You will find more info at:
https://firebase.google.com/docs/reference/js/firebase.database.Query#on
u can also use firebase cloud functions as well by putting a trigger, so that this can be handled by server.
export const onNewChatTrigger = functions.database.ref('chat/{chatId}').onCreate(event => {
let key = event.params.chatId;
let data = event.data.val();
...
});

Firebase dynamic object key

So I want the key in my firebase database to be dynamically generated.
At the moment, I have something like this
whatever.$add({
title: $scope.formData.title
})
UPDATE: The whatever is actually a $firebaseArray, and yes, returns an id.
Actually, in the above example, I wanted to do something like:
whatever.$add({
$scope.formData.type: $scope.formData.title
})
I basically want the key set to something that'll come from a form. Any way?
I'm supposing that whatever is actually a $firebaseArray and if i'm right your $add will always result in a new child with random id.
If you want to create a new child with a custom id you should be working with .child().set():
var ref = new Firebase(yourFirebaseUrl);
ref.child(customId).set({
type: $scope.formData.title
});
Update:
To have the $scope.formData.title as the id you should do:
var ref = new Firebase(yourFirebaseUrl);
ref.child($scope.formData.title).set({
type: $scope.formData.title
anotherData: $scope.formData.anotherFormData
});

AngularFire extending the service issue

I've been looking at the documentation for Synchronized Arrays https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-extending-the-services and https://www.firebase.com/docs/web/libraries/angular/guide/extending-services.html#section-firebasearray
I'm using Firebase version 2.2.7 and AngularFire version 1.1.2
Using the code below, I'm having trouble recognizing $$removed events.
.factory("ExtendedCourseList", ["$firebaseArray", function($firebaseArray) {
// create a new service based on $firebaseArray
var ExtendedCourseList= $firebaseArray.$extend({
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
console.log("new course");
return course;
},
$$removed: function(snap){
console.log("removed");
return true;
}
});
return function(listRef) {
return new ExtendedCourseList(listRef);
}
}])
.factory("CompanyRefObj", function(CompanyRef) {
//CompanyRef is a constant containing the url string
var ref = new Firebase(CompanyRef);
return ref;
})
.factory('CourseList', function (localstorage,$rootScope,ExtendedCourseList,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = ExtendedCourseList(CompanyRefObj.child(companyID).child("courses"));
)
If I run this code, only the $$added events will be triggered. To simulate the remove events I use the web-interface at Firebase to display data, where I press the remove button and accept the data being deleted permanently.
Additionally, if I delete the $$removed function, the extended service still won't synchronize when a record is deleted.
If I modify my code to use the $firebaseArray instead of extending the service (as seen above) both add and remove events will be recognized.
.factory('CourseList', function (localstorage,$rootScope,$firebaseArray,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = $firebaseArray(CompanyRefObj.child(companyID).child("courses"));
)
Finally, are there any bad practices I've missed that can cause some of the extended functions to not work?
Solved
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
//Modified below
course.$id = course_key;
//End of modification
console.log("new course");
return course;
}
After posting about the issue at firebase/angularfire github I received an answer that solved my issue. When $$added got overridden by the code provided, the $firebaseArray also lost its internal record $id.
Adding this line of code: course.$id = course_key; before returning the course, made AngularFire recognize when the record was removed from the server.

Prevent items in scope from writing to a different user's records

I was having success with using AngularFire in a scenario where there is one user on my application.
Now that I have authentication up and running, I'm noticing that assigning items to $scope.items is catastrophic when switching users, mainly due to the $scope failing to update correctly.
Reading directly from the docs...
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items');
angularFire(ref, $scope, 'items');
I need these to be only the items of the currently authorized user. So currently, I do this (if there's a better way, don't hesitate to tell me!)
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/userId');
angularFire(ref, $scope, 'items');
I generate userId using auth.provider and auth.id, btw. Now that my items are namespaced in (let's say) user1
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/[user1id]');
angularFire(ref, $scope, 'items');
I add items to $scope.items
$scope.create = function(item) {
$scope.items.push(item)
/* Pretend the user adds these from the interface.
[
{ name: 'eenie' },
{ name: 'meenie' },
{ name: 'miney' },
{ name: 'moe' }
]
*/
}
The problem
Now if I just log out and login as someone else, magically that user has eenie meenie miney and moe because $scope.items held the array between logout and login.
I tried to set $scope.items = [] on logout event, but that actually empties all the records. I'm pulling my hair out. This is 0.001% of what I need to do in my project and it's taking my whole weekend.
Update New method
$scope.create = function() {
$scope.selectedDevice = {
name: 'New Device',
userId: $scope.user.provider + $scope.user.id
};
return $scope.devices.push($scope.selectedDevice);
};
$scope.$on('angularFireAuth:login', function(evt, user) {
var promise, ref;
ref = new Firebase('https://mysite.firebaseio.com/users/' + (user.provider + user.id) + '/registry/');
promise = angularFire(ref, $scope, 'devices');
});
It now will accurately create items under the user's id. However, still, once you logout and log back in, those items do not get cleared from $scope.devices. Therefore, they just add themselves to data but under the newly logged in user.
Update
I did a lot of trial and error. I probably set $scope.devices to [] and moved around login events in every possible combination. What eventually worked was #hiattp's fiddle in the accepted answer.
This is a result of the implicit data binding remaining intact as you switch users. If the new user shows up and creates a new binding, it will consider the existing data to be local changes that it should assimilate (that's why you see the original user's items being added to the new user), but if you try to clear them first without releasing the binding then you are implicitly telling Firebase to delete that data from the original user's item list (also not what you want). So you need to release the data bindings when you detect the logout (or login) events as needed.
The callback in the angularFire promise provides an "unbind" method (see here and here):
var promise = angularFire(ref, $scope, 'items');
promise.then(function(unbind){
// Calling unbind() will disassociate $scope.items from Firebase
// and generally it's useful to add unbind to the $scope for future use.
});
You have a few idiosyncrasies in your code that are likely causing it not to work, and remember that unbind won't clear the local collection for you. But just so you have an idea of how it should work (and to prove it does work) here is a fiddle.
You need to unbind $scope.items on logout. The best way to do this will be to save the unbind function given to your promise in $scope:
var ref = new Firebase('https://<my-firebase>.firebaseio.com/items/[user1id]');
angularFire(ref, $scope, 'items').then(function(unbind) {
$scope.unbindItems = unbind;
});
$scope.$on('angularFireAuth:logout', function() {
$scope.unbindItems();
});

How to retrieve only new data?

I'm trying to create simple notification system for my site admin, and I need to send only real-time messages to every admin user. But when I use firebase it loads old data on every page, and user see all messages from database. If I set limit(1) user will see last notification on every page reloading:
var eventsList = new Firebase('https://*****-messages.firebaseio.com/');
eventsList.on('child_added', function(message) {
var message = message.val();
$.notification(message.message);
});
How I can load only new messages, without old notification history?
This is by design, in a real-time system there is no concept of the "latest" data because it's always changing. However, if you want to only display items added to the list after the page has loaded, you can do the following:
var newItems = false;
var eventsList = new Firebase('https://*****-messages.firebaseio.com/');
eventsList.on('child_added', function(message) {
if (!newItems) return;
var message = message.val();
$.notification(message.message);
});
eventsList.once('value', function(messages) {
newItems = true;
});
I would comment on the above, but due to reputation I cannot, so hoping this is adequate and this is to address the last comment by Gruff McGruff.
Once fires after because you want to break out of the loop of grabbing all child items. Once that loops is broken, you'll set the newItems variable to true, and then it will be able to get all new children after that.
If you fired it before, it would defeat the purpose and grab all child items regardless because you'll set the newItems variable immediately.
Also, I've used this approach and it works well.

Resources