Firebase remove function do not work - angularjs

I'm building an app with Firebase and AngularJS and I have a table with my users.
From one of my view I want to create a form permit to delete users from the Firebase table .
So I have a drop down menu with my users names and submit button.
I wrote a function to retrive the name of the user from the form and combine it with my url location of the user table, in fact the table has user name as id :
$scope.Delete_user = function(name) {
var testRef = new Firebase("https://alex-jpcreative.firebaseio.com/users")
var newRef = testRef + '/' + name;
$scope.removeUser(newRef);
}
In this function I called the removeUser one that is a function I found in Firebase doc to delete a item from the table :
$scope.removeUser = function(ref) {
ref.remove(function(error) {
alert(error ? "Uh oh!" : "Success!");
});
}
I can see the first function working fine pass the right name of users and combine it with the URL but then I have this error and it doesn't work:
TypeError: Object https://alex-jpcreative.firebaseio.com/users/Alex_dev_JPC has no method 'remove'

You need to use the child method to get the reference to the user object, rather than just appending the string to the end:
$scope.Delete_user = function(name) {
var testRef = new Firebase("https://alex-jpcreative.firebaseio.com/users");
var newRef = testRef.child(name);
$scope.removeUser(newRef);
}
See the Firebase documentation for more details.

Related

Firebase onDisconnect not deleting user name on disconnection

Perhaps I am mis-using onDisonnect(), but I looked at the example code on the firebase.blog and am doing my best.
When a user submits a user name, I call the code below, which adds the username to a firebase db. Then on disconnection, I want the username to be deleted from the db. This would mean that the db would only show users that are connected to the app at that moment in time.
I am doing it this way so I can then call the data and then map through the array to display currently logged-in users.
I have made two attempts in deleting the name, which you can see in the code below under con.onDisconnect().remove();, neither of which work the way I need. That said, if I log in once again from the same computer, the first user name replaces the second user name!
Here is my code
setName = e => {
e.preventDefault()
let name = this.state.name;
let connectedRef = firebase.database().ref('.info/connected');
connectedRef.on('value', function (snap) {
if (snap.val() === true) {
// Connected
let con = myConnectionsRef.push();
myConnectionsRef.set({
name
})
// On disconnect
con.onDisconnect().remove();
myConnectionsRef.orderByChild('name').equalTo(name).once('child_added', function (snapshot) {
snapshot.ref.remove();
// var nameRef = firebase.database().ref('users/'+name);
// nameRef.remove()
})
}
});
Where am I going wrong? Is there a better way to use onDisconnect? From the example on the fb forum, it isn't clear where I would put that block of code, hence why I am attempting to do it this way.
Thanks.
If I understand correctly what is your goal, you don't need to do
myConnectionsRef.orderByChild('name').equalTo(name).once('child_added', function (snapshot) {
snapshot.ref.remove();
// var nameRef = firebase.database().ref('users/'+name);
// nameRef.remove()
})
as the onDisconnect().remove() call will take care of that.
Also, as explained in the blog article you refer to (as well as shown in the doc):
The onDisconnect() call shall be before the call to set() itself. This is to
avoid a race condition where you set the user's presence to true and
the client disconnects before the onDisconnect() operation takes
effect, leaving a ghost user.
So the following code should do the trick:
setName = e => {
e.preventDefault()
let name = this.state.name;
const connectedRef = firebase.database().ref('.info/connected');
const usersRef = firebase.database().ref('users');
connectedRef.on('value', function (snap) {
if (snap.val() === true) {
// Connected
const con = usersRef.child(name); //Here we define a Reference
// When I disconnect, remove the data at the Database location corresponding to the Reference defined above
con.onDisconnect().remove();
// Add this name to the list of users
con.set(true); //Here we write data (true) to the Database location corresponding to the Reference defined above
}
});
The users node will display the list of connected users by name, as follows:
- users
- James: true
- Renaud: true

Return a firebase database reference as an array in a cloud function

Below is the firebase database reference returned as an array in my AngularJS scope:
var ref = firebase.database().ref("users").child(user.uid).child("week1");
$firebaseArray(ref);
However when I tried writing the same code in my index.js file for a database Cloud Function, there was an error message:
ReferenceError: $firebaseArray is not defined at /user_code/index.js:22:18
Is there a way to make a Firebase reference ref return as an array in my index.js Cloud Functions file since $firebaseArray is not defined outside the AngularJS scope?
Below is an illustration of the database:
users: {
user uid (generated by push) : {
deviceToken : "tokenID",
name : "Display Name"
},
anotherUserID : {
deviceToken : "tokenID",
name : "Display Name"
},
Players: {
player1: John,
Player2: ken,
}
Is there a way for a change in the Players database node to trigger a function in the users node (for each user):
exports.update = functions.database.ref('/Player')
.onWrite(event=>{
console.log(event.data);
var ref = admin.database().ref('/users/'+ user.uid+ '/week1');
ref.set(10);
return;
});
My issue was accessing the user.uid (created by the push() method) for each user.
In Cloud Functions, you can use the Firebase Admin SDK to save and retrieve data from the database. To initialize the Admin SDK, you can use environment configuration:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
You can then attach a value listener to your database reference, utilizing and returning a JavaScript Promise to keep the function alive and chain your logic:
const ref = admin.database().ref("/users").child(user.uid).child("week1");
return ref.once("value").then(snapshot => {
const values = snapshot.val();
// Do something with the snapshot
});
The values variable here will be assigned an array of the values at the location (see val()), so could be used in place of $firebaseArray.
If you need to obtain a reference to the node that triggered your database function, you can use the event parameter from the function definition:
let ref = event.data.ref;
This is useful if you need to obtain the specific location in the database that triggered your function. The event.data variable is a DeltaSnapshot instance for database functions.
Similarly, you can use event.params to obtain any of the variables specified in a database trigger definition, for example {uid} in the below definition can be obtain using event.params.uid:
exports.example = functions.database.ref('/users/{uid}')
.onWrite(event => {
const uid = event.params.uid;
// ...
});
The method you take here depends on what work you need your function to perform, how it's triggered and what variables or values you need access to.
Remember that your function can freely read and write data anywhere in the database using the Admin SDK too.
In your newest example, it would be hard to match a node at /players to /users because there isn't a value with the user's UID. So you would need to change the /players child nodes to include further data, something like:
players : {
player1 : {
name: "John",
uid: "anotherUserID"
}
}
You could then extract this uid using event.data.child('uid').val() from your function (where your function is triggered by children under this node using /players/{playerId}):
exports.update = functions.database.ref('/players/{playerId}')
.onWrite(event=>{
console.log(event.data);
const uid = event.data.child('uid').val();
var ref = admin.database().ref('/users/' + uid + '/week1');
ref.set(10);
return;
});

SocketIO syncUpdate model for different user

I have been writing an web app, where individual user will post their task and will be displayed on their news feed. I have used socket io with angular fullstack framework by yeoman.
what I am trying to do is, when user adds new data to goal model, on socketSyncUpadtes, $rootScope.getUserGoals get updated and user see new entry. but I want that to happen for each user, thats why I included userid, to make sure, it only pulls user specific data.
But in reality what is happening is, all connected user getting this socket stream and getting their model updated too, but as page refreshed it is always only what that query pulls, only user specific data.
this is how I get user specific data
$rootScope.getUserGoals = function (userid){
//get current todo task views
$http.get('/api/goals/name/'+userid).success(function(goals) {
$rootScope.userGoalArr = goals;
socket.syncUpdates('goal', $rootScope.userGoalArr);
});
};
then it displays in view with this
<ul class = "holdTaskul" ng-repeat="view in filtered = userGoalArr | orderBy:'-created'">
I looked up on google and search a lot to figure out how to separate each user socket stream and attached them to their login credential. I have come across this socket-jwt and session token. I am not expert on socket. I would much appreciate if someone could point me to right direction about this, what I am intending to do..
my socket configuration is as follows for socket.syncUpdate function
syncUpdates: function (modelName, array, cb) {
cb = cb || angular.noop;
/**
* Syncs item creation/updates on 'model:save'
*/
socket.on(modelName + ':save', function (item) {
var oldItem = _.find(array, {_id: item._id});
var index = array.indexOf(oldItem);
var event = 'created';
// replace oldItem if it exists
// otherwise just add item to the collection
if (oldItem) {
array.splice(index, 1, item);
event = 'updated';
} else {
array.push(item);
}
cb(event, item, array);
});
/**
* Syncs removed items on 'model:remove'
*/
socket.on(modelName + ':remove', function (item) {
var event = 'deleted';
_.remove(array, {_id: item._id});
cb(event, item, array);
});
},
I have also configured socket for sending auth token by this
// socket.io now auto-configures its connection when we ommit a connection url
var ioSocket = io('', {
// Send auth token on connection, you will need to DI the Auth service above
query: 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
and used socketio-jwt to send the secret session to socket service by that
module.exports = function (socketio) {
socketio.use(require('socketio-jwt').authorize({
secret: config.secrets.session,
handshake: true
}));

AngularFire: Getting the ID of Users Data from email in SimpleLogin

I'm trying to implement some simple using registration using Firebase through AngularFire and Angular.js. I'm using the SimpleLogin tool to manage the users. I can create users just fine.
var firebaseRef = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseSimpleLogin(firebaseRef);
var firebaseUsersRef = new Firebase(FIREBASE_URL + 'users');
var firebaseUsers = $firebase(firebaseUsersRef);
var myObject = {
register: function(user) {
var myDate = new Date().getTime();
return simpleLogin.$createUser(
user.email, user.password)
.then(function(regUser) {
var userInfo = {
date: myDate,
md5: regUser.md5_hash,
firstname: user.firstname,
lastname: user.lastname,
email: user.email
}
firebaseUsers.$push(userInfo).then(function(ref) {
userInfo.uid = ref.name();
$rootScope.currentUser = userInfo;
});
}); //push user
}, //register
Works like a charm. In order to get at this information when the user logs in, I've tried implementing an event handler on the $rootscope. I would like it to search through the uid that I stored and then get me record with the right user information.
$rootScope.$on('$firebaseSimpleLogin:login', function (e, authUser) {
var query = $firebase(firebaseRef.startAt(authUser.uid).endAt(authUser.uid));
console.log(query);
$location.path('/meetings');
});
In order to use startAt and endAt, do I have to establish $priority. When I try, I get an error stating that I can't have any special characters. So that never works. I don't really care about how this data stored, I just want to get the index of the data so that I can retrieve the right user.
By using $push you tell Firebase to generate a key for you.
This is great for collections where you normally access all children at the same time. But that is not the case for your user info: you want to access the info for the current user.
So instead of using $push to add your user's info, I would use the uid of the user.
In the regular Firebase JavaScript API this can be accomplish with:
firebaseUsersRef.child(reguser.uid).set(userInfo);
The equivalent in AngularFire probably uses $set, but I don't think you have any need for that in your $createUser callback.
Update
It looks like you're trying to add your info to the existing user node that Firebase creates for you. This is the example from that from the Firebase documentation on storing user data:
myRef.child('users').child(user.uid).set({
displayName: user.displayName,
provider: user.provider,
provider_id: user.id
});
You can see that they do access the user's node using child(user.uid) similar to what I proposed.
Lessons
Two relatively small mistakes here as far as I can see:
when you use push/$push, you let Firebase generate the node name for you. In cases where there already is a globally unique ID (such as the uid of a user), you're often better off using that as the node name.
If you know the name of the node you want to retrieve, you don't need a query. You can simply access the node as ref.child(user.uid).
Thanks to Frank, I was able to figure out the right way to do this. In order to make my own users object searchable, I can use the uid from the simpleLogin object. So my register function works like this:
var firebaseRef = new Firebase(FIREBASE_URL);
var simpleLogin = $firebaseSimpleLogin(firebaseRef);
var myObject = {
register: function(user) {
var myDate = new Date().getTime();
return simpleLogin.$createUser(user.email, user.password)
.then(function(regUser) {
var userInfo = {
date: myDate,
md5: regUser.md5_hash,
firstname: user.firstname,
lastname: user.lastname,
email: user.email
}
firebaseUsers.$set(regUser.uid, userInfo);
}); //add user
}, //register
} //my Object
Using set instead of push, I can store the uid from the registered user into the object and then pass along what I want to add as the second parameter. My database will now have the users organized by uid, which can be accessed via a url.
Then, when users log in, Firebase will throw up a login event along with the authenticated user, which I can catch and use to add the current user to the $rootScope that's accessible throughout my application.
$rootScope.$on('$firebaseSimpleLogin:login', function (e, authUser) {
var ref = new Firebase(FIREBASE_URL + '/users/' + authUser.uid);
var user = $firebase(ref).$asObject();
user.$loaded().then(function() {
$rootScope.currentUser = user;
});
$location.path('/meetings');
});
Thanks for pointing me in the right direction Frank.

firebase/angularFire querying with startAt/endAt doesn't work

I have an app where I use FirebaseSimpleLogin, but since I have additional user data, I store it under [my-firebase].firebaseio.com/users/[username]. The snippet below shows how it's done
var User = {
create: function (authUser, username) {
users[username] = {
md5_hash: authUser.md5_hash,
username: username,
email: authUser.email,
$priority: authUser.uid
};
users.$save(username).then(function () {
setCurrentUser(username);
});
},
...
Since data for each individual user, are keyed based on username I prioritize by uid, so I can later fetch additional user data by uid.
When the firebase login event fires, I have the following handler, that is responsible for querying firebase to get the additionala user data, and store it on $rootScope.currentUser via a method setCurrentUser(username)
Here is my login event handler:
var ref = new Firebase(FIREBASE_URL + '/users');
var users = $firebase(ref);
$rootScope.$on('$firebaseSimpleLogin:login', function (e, authUser) {
var query = $firebase(ref.startAt(authUser.uid).endAt(authUser.uid));
query.$on('loaded', function () {
//PROBLEM: console.log(query.$getIndex()) logs an empty array
setCurrentUser(query.$getIndex()[0]);
});
});
As soon as the login event fires, I get access to the authUser, which contains the uid of the logged in user. Then I query firebase /users by uid, using startAt/endAt to limit my results so that I'm left only with the currently logged in user.
Then, when the query data is loaded from firebase, I invoke the setCurrentUser method which stores the username of the currentUser on $rootScope.
The Problem
The query filtering using startAt/endAt does not work, I'm getting back an empty array when I console.log query.$getIndex() when I should be getting an array with the username of the currently logged-in user.
I'm using firebase 1.0.15, angularFire 0.7.1 and firebase-simple-login 1.4.1 and following this tutorial from Thinkster.io
In the end somewhere in my code I had a $save() on my user binding, to save the id of the last project they've been working on, and this was causing the $priority issue. I used $update instead, and now everything works like a charm!
Still I don't know if this is intended behavior of $save()
To clarify and provide more context, I wasn't passing any keys to the $save() method, I simply added a property on my local user reference, and saved it
$rootScope.currentUser.lastWorkingProject = projectId;
$rootScope.currentUser.$save();

Resources