Firebase on(child_added) some field 'undefined' - firebase

I am working on a real time application and i am using firebase with pure html and javascript (not angularJS).
I am having a problem where i saved user's data to firebase with the given code by firebase :
var isNewUser = true;
ref.onAuth(function(authData) {
if (authData && isNewUser) {
authData['status'] = 'active';
authData['role'] = 'member';
ref.child("users").child(authData.uid).set(authData);
}
});
This will add the authData to the /users/ node. As you can see that i also appended some custom fields to the authData, status and role.
Now i am using this code to get the user's data from firebase and display them.
ref4.on("value", function(snapshot) {
var snapshotData = snapshot.val();
console.log('username: '+snapshotData.status);
});
If i use on('value'), the status get printed out on the console but if i do it this way,
ref4.on("child_added", function(snapshot) {
var snapshotData = snapshot.val();
console.log('status: '+snapshotData.status);
});
It is showing undefined for the status. May i know what's wrong and how to fix this problem. Thank you.

Since value is returning the path provided by ref4, and child_added is returning each child of that path, it's unlikely both are going to have a key status.
Consider this data structure:
{
"users": {
"brucelee": {
"status": "awesome"
},
"chucknorris": {
"status": "awesomerest"
}
}
}
If I now query for this according to your incomplete example:
var ref = new Firebase('https://<instance>firebaseio.com/users/brucelee');
ref.on('value', function(snap) {
// requests the brucelee record
console.log(snap.name(), ':', snap.val().status); // "brucelee: awesome"
});
ref.on('child_added', function(snap) {
// iterates children of the brucelee path (i.e. status)
console.log(snap.name(), ':', snap.val().status); // THROWS AN ERROR, because status is a string
});
So to do this on child_added with a data structure like this (and presumably somewhat like yours), it would look as follows:
ref.on('child_added', function(snap) {
// iterates children of the brucelee path (i.e. status)
console.log(snap.name(), ':', snap.val()); // "status: awesome"
});

Related

Tracker autorun using findone

I have this piece of code in client side:
Tracker.autorun(function () {
if (params && params._id) {
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
}
}
});
params will be passed into the url. So, initially we won't have the department data and the findOne method will return null, and then later on, when data arrives, we can find the department object.
But if user enters an invalid id, we need to return them 404. Using tracker autorun, how can I distinguish between 2 cases:
a. Data is not there yet, so findOne returns null
b. There is no such data, even in server's mongodb, so findOne will also returns null.
For case a, tracker autorun will work fine, but for case b, I need to know to return 404
I would suggest you to subscribe to data inside template, like below so you know when subscriptions are ready, then you can check data exists or not
Template.myTemplate.onCreated(function onCreated() {
const self = this;
const id = FlowRouter.getParam('_id');
self.subscribe('department', id);
});
Template.myTemplate.onRendered(function onRendered() {
const self = this;
// this will run after subscribe completes sending records to client
if (self.subscriptionsReady()) {
const id = FlowRouter.getParam('_id');
const dept = Department.findOne({ _id: params._id }) || Department.findOne({ name: params._id });
if (dept) {
// found data in db
} else {
// 404 - no department found in db
}
}
});
If you are using Iron-Router, you may try this hack.
Router.route('/stores', function() {
this.render('stores', {});
}, {
waitOn: function() {
return [
Meteor.subscribe('stores_db')
];
}
});
The sample code above will wait for the subscription "stores_db" to complete, before rendering anyhing. Then you can use your findOne logic no problems, ensuring that all documents are availble. This suits your situation.
This is what I used to do before I completely understand MeteorJS publications and subscriptions. I do not recommend my solution, it is very bad to user experience. Users will see the page loading forever while the documents are being download. #Sasikanth gave the correct implementation.

Meteor subscription is not stopping

I've got what should be a relatively simple issue. I set a session, then a subscribe to a collection using the string stored in the session. But when that session changes, I need to clear the subscription data and start again.
My code is as follows:
let subscriptionReady;
let filteredResults = [];
let rawResults = [];
let county = Session.get('county');
let type = Session.get('type');
This is mostly just prep work to create some empty objects to populate later. This all gets set on a click event. After we set these placeholder objects we go and subscribe by those sessions:
if (county && !type) {
return function() {
if (subscriptionReady) {
subscriptionReady.stop();
}
filteredResults = [];
rawResults = [];
subscriptionReady = Meteor.subscribe('resourcesearch', county, {
onReady: () => {
rawResults = resourceCollection.find({}, { sort: {score: -1} }).fetch();
rawResults.forEach((result) => {
if (result.score) {
filteredResults.push(result);
}
});
}
});
}
At the third line I run a check to see if subscriptionReady exists, then it will have the stop method available. So then I run it. But, it doesn't actually stop anything.
What am I missing?
After trial and error, I've got it solved. The issue was the placement of the stop call. I no longer have to check if subscriptionReady exists, instead I stop the subscription inside of the onReady method:
return function() {
filteredResults = [];
rawResults = [];
subscriptionReady = Meteor.subscribe('resourcesearch', county, {
onReady: () => {
rawResults = resourceCollection.find({}, { sort: {score: -1} }).fetch();
rawResults.forEach((result) => {
if (result.score) {
filteredResults.push(result);
}
});
subscriptionReady.stop();
}
});
It's .stop() not .stop docs
Also you can probably avoid your filtering loop by including score in your query. Are you looking for documents where the score key exists {score: {$exists: true}} or just where it is non zero {$score: {$ne: 0}}?
Also you shouldn't need to clear the subscription and start again. If you make your subscription parameter resourcesearch a reactive data source then the subscription will automatically update to give you the documents you need. Starting/stopping a subscription in response to a search would be an anti-pattern.

Getting a username from ID without autopublish

I just got done with the rough draft of my app, and thought it was time to remove autopublish and insecure mode. I started transfering all the stray update and insert methods I had been calling on the client to methods. But now I'm having trouble returning a username from an ID.
My function before: (that worked, until I removed autopublish)
challenger: function() {
var postId = Session.get('activePost');
var post = Posts.findOne(postId);
if (post.challenger !== null) {
var challenger = Meteor.users.findOne(post.challenger);
return challenger.username;
}
return false;
}
Now what I'm trying:
Template.lobby.helpers({
challenger: function() {
var postId = Session.get('activePost');
var post = Posts.findOne(postId);
if (post.challenger !== null) {
var userId = post.challenger;
Meteor.call('getUsername', userId, function (err, result) {
if (err) {
console.log(err);
}
return result;
});
}
return false;
},
Using:
Meteor.methods({
getUsername: function(userId) {
var user = Meteor.users.findOne({_id: userId});
var username = user.username;
return username;
},
...
})
I have tried blocking the code, returning values only once they're defined, and console.logging in the call-callback (which returned the correct username to console, but the view remained unchanged)
Hoping someone can find the obvious mistake I'm making, because I've tried for 3 hours now and I can't figure out why the value would be returned in console but not returned to the template.
Helpers need to run synchronously and should not have any side effects. Instead of calling a method to retrieve the user, you should ensure the user(s) you need for that route/template are published. For example your router could wait on subscriptions for both the active post and the post's challenger. Once the client has the necessary documents, you can revert to your original code.

Am I using ForEach correctly?

I'm working on a presence-like system in firebase with following layout:
firebase {
user1 {
isOnline: true
}
user 2 {
isOnline: true
}
user3 {
isOnline: false
}
}
The isOnline booleans are what I am going to use later to output the names of the users that are online to the console
So for example, in the case above it would say:
user1 is online.
user2 is online.
Here is my code:
var gameRef = new Firebase("https://xxx.firebaseio.com/");
var userOnline = new Firebase('https://xxx/.info/connected');
userOnline.on('value', function (snapshot) {
if (snapshot.val()) {
gameRef.child(user).update({
isOnline : true
});
}
else {
gameRef.child(user).update({
isOnline : false
});
}
});
// for each user that is online, output to the console
gameRef.forEach(function (snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
});
There seems to be a problem with my forEach, how can I fix this?
Thanks.
You cannot forEach over a ref, but only over a snapshot.
// for each user that is online, output to the console
gameRef.on('value', function(function(gamesSnapshot) {
gamesSnapshot.forEach(function (snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
}
});
This code has two snapshot variables:
gameSnapshot is the data in the parent node
snapshot is the data of a specific player
Alternative
The approach above will download all players, even though you are only looking to deal with players that are online. It is more efficient in this case, to query Firebase so that it only returns players that are online.
// for each user that is online, output to the console
var onlinePlayers = gameRef.orderByChild('isOnline').equalTo(true);
onlinePlayers.on('child_added', function(function(snapshot) {
var obj = snapshot.val();
if(obj.isOnline == true) {
console.log(obj.name + " is online.");
}
});
The code now listens for the child_added event, since Firebase spoon-feeds us the players one at a time. You will probably also have to handle child_changed and child_removed, once you map the players to HTML elements.
Even though this will result in a bit more code, I would normally recommend using querying and the child_* events, since they limit the data that Firebase sends you both initially and when e.g. a player goes offline.

Firebase Displaying Other Users' username except yours Using Presence

Hi I'm new to firebase and was trying out the presence example on firebase everything is working normal. My issue is how do I display the username of others ONLY because everything I cant seem to find the solution for this because
I tried googling for an answer but none of the results are what I'm looking for.
I'm new to Firebase and non-mysql database so I dont know how to do a WHERE Statement on firebase
here is my code:
<body>
<div id="presenceDiv" class="l-demo-container example-base">
</div>
<script>
var name = "<?php echo $uname;?>";
var currentStatus = "★ online";
// Get a reference to the presence data in Firebase.
var userListRef = new Firebase("https://<URL>.firebaseio.com/");
// Generate a reference to a new location for my user with push.
var myUserRef = userListRef.push();
// Get a reference to my own presence status.
var connectedRef = new Firebase("https://<URL>.firebaseio.com//.info/connected");
connectedRef.on("value", function(isOnline) {
if (isOnline.val()) {
// If we lose our internet connection, we want ourselves removed from the list.
myUserRef.onDisconnect().remove();
// Set our initial online status.
setUserStatus("★ online");
}
else {
// We need to catch anytime we are marked as offline and then set the correct status. We
// could be marked as offline 1) on page load or 2) when we lose our internet connection
// temporarily.
setUserStatus(currentStatus);
}
});
// A helper function to let us set our own state.
function setUserStatus(status) {
// Set our status in the list of online users.
currentStatus = status;
myUserRef.set({ name: name, status: status });
}
function getMessageId(snapshot) {
return snapshot.name().replace(/[^a-z0-9\-\_]/gi,'');
}
// Update our GUI to show someone"s online status.
userListRef.on("child_added", function(snapshot) {
var user = snapshot.val();
$("<div/>")
.attr("id", getMessageId(snapshot))
.text(user.name + " is currently " + user.status)
.appendTo("#presenceDiv");
});
// Update our GUI to remove the status of a user who has left.
userListRef.on("child_removed", function(snapshot) {
$("#presenceDiv").children("#" + getMessageId(snapshot))
.remove();
});
// Update our GUI to change a user"s status.
userListRef.on("child_changed", function(snapshot) {
var user = snapshot.val();
$("#presenceDiv").children("#" + getMessageId(snapshot))
.text(user.name + " is currently " + user.status);
});
// Use idle/away/back events created by idle.js to update our status information.
document.onIdle = function () {
setUserStatus("☆ idle");
}
document.onAway = function () {
setUserStatus("☄ away");
}
document.onBack = function (isIdle, isAway) {
setUserStatus("★ online");
}
setIdleTimeout(5000);
setAwayTimeout(10000);
</script>
</body>
</html>
This script keeps on loading my 1st dummy username along the other dummy users that i tried logging on with. The same goes for the other dummy accounts the browser loads their username along with the others.. Whats causing this and how do I solve it? Please help
I'd simply identify and exclude the current user in you on(child_ handlers.
So for example:
// Update our GUI to show someone"s online status.
userListRef.on("child_added", function(snapshot) {
var user = snapshot.val();
if (user.name != name) {
$("<div/>")
.attr("id", getMessageId(snapshot))
.text(user.name + " is currently " + user.status)
.appendTo("#presenceDiv");
}
});

Resources