Meteor user name based on userId - meteor

I am saving logged in userId with each record saved in my Meteor app collection as shown in the example below, yet I was wondering if there was any way in Meteor where I can retrieve user name based on the user saved id without have to make another query on the users collection? In Node.js / mongoose there was this Populate function, but I can't seem to find similar package / function in Meteor. So I was wondering if someone can help me by suggesting a resolution to this problem (if any). thanks
var newInvoice = {
customerid: $(e.target).find('[name=customer]').val(),
userid: Meteor.userId(),
//....more fields here
}
Meteor.call('saveInvoice', newInvoice, function(error, id){
if(error)
return alert(error.reason);
});

Related

Firebase - How do I store user data so that I can easily fetch it by their email? [duplicate]

I have the following structure on my Firebase database:
I would like to search for a user by name, last name or email but as I don't have the user key in the level above I don't know how I can achieve this. I'm doing and administrator session so it wouldn't have access to the user key.
I have tried:
let usersRef = firebase.database().ref('users');
usersRef.orderByValue().on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});
But it brings all the users on the database. Any ideas?
You can use equalTo() to find any child by value. In your case by name:
ref.child('users').orderByChild('name').equalTo('John Doe').on("value", function(snapshot) {
console.log(snapshot.val());
snapshot.forEach(function(data) {
console.log(data.key);
});
});
The purpose of orderByChild() is to define the field you want to filter/search for. equalTo() can get an string, int and boolean value.
Also can be used with auto generated keys (pushKey) too.
You can find all the documentation here
A warning to avoid unpleasant surprises: when you use orderByChild and equalTo do not forget to add an index on your data (here's the doc)
If you don't all the nods will be downloaded and filtered client side which can become very expensive if your database grows.

Angularfire 2.1 - How to access auto-generated ID for users (or how to make my UID the first node for each user)

Disclaimer, I am trying to self-teach myself development. I am building a hybrid mobile app using Ionic 1 and now Firebase 3 for my database and authentication.
For my scenario, in short, I'm trying to display a list of 'friends' for the user that is currently logged in. Here is the current data structure I have (the relevant part anyway):
Data Structure
I have a line of code that does return me what I want:
var friends = $firebaseArray(ref.child('users').child('-KXcxMXkKs46Xv4-JUgW').child('friends'));
Of course, that can't work because there is a nice little hard coded value in there.
So, I looked into how to retrieve the current UID so I could replace the hard coded value. But after running the following bit of code through, the first node under user is not the UID (it is some other auto generated value that I don't really know how it got there). The UID is actually within the id field.
var ref = firebase.database().ref();
authObj = $firebaseAuth();
var firebaseUser = authObj.$getAuth();
console.log(firebaseUser.uid);
So, ultimately what I would love is to be able to change the data structure so that the UID is the first node under Users, but I can't seem to find documentation to do that. I looked at this other stack thread, but it is for an outdated version and I can't seem to connect the dots. Other thread
Though, if I can't change the structure, I still need to figure out how to access that friends node for the current user, one way or another.
Thank you in advance. This is my first stackoverflow post, so be gentle.
Update:
Per Frank's comment, this is the code that I execute to create users - $add is what is creating the push id (-KXcxM...).
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email,
registered_in: Date()
// a number of other things
};
var messagesRef = $firebaseArray(firebase.database().ref().child("users"));
messagesRef.$add(profile);
},
register: function(user) {
return auth.$createUserWithEmailAndPassword(user.email, user.password)
.then(function(firebaseUser) {
console.log("User created with uid: " + firebaseUser.uid);
Auth.createProfile(firebaseUser.uid, user);
Utils.alertshow("Success!","Your user has been registered.");
})
.catch(function(error) {
Utils.alertshow("Error.","Some helpful error message.");
console.log("Error: " + error);
});
}
Instead of creating a $firebaseArray and calling $add on it, you can just store the user using the regular Firebase JavaScript SDK:
createProfile: function(uid, user) {
var profile = {
id: uid,
email: user.email
};
firebase.database().ref().child("users").child(uid).set(profile);
}
Since AngularFire is built on top of the Firebase JavaScript SDK, the two interact nicely with each other. So if you have any existing $firebaseArray on users it will pick up the new profile too.

Fetch data for record edit page

I have a page that lets you edit user data. I'm using FlowRouter for the routing and it can be found on the route /employees/:id.
I need to update the detail form when data changes on the server and leave the route if it was deleted by other client.
I decided to use Tracker.autorun which informs me whenever the data changes. The previous user info is stored on the template so it's easy to tell if the record was deleted.
Template.UpdateEmployee.onCreated(function () {
const self = this;
self.subscribe('user', FlowRouter.getParam('id'));
self.autorun(function () {
const _id = FlowRouter.getParam('id');
const user = Meteor.users.findOne({_id});
if(!user && self.user)
FlowRouter.go('/employees');
self.user = user;
if(!user)
return;
user.email = user.emails[0].address;
$('.ui.form').form('set values',user);
});
});
And lastly in the onRendered callback I'm checking if the data was set on template as I believe not doing so could lead to data being available before the template is rendered and hence values wouldn't get set properly. Is this correct?
Template.UpdateEmployee.onRendered(function () {
if(this.user){
user.email = user.emails[0].address;
$('.ui.form').form('set values',user);
}
});
Are there any pitfalls to this solution?
I can see a couple drawbacks inherently. The first one is doing a find query on the client. Typically you would want to return data from the server using Meteor publish and subscribe.
The second is you are passing the key to find the data over the URL. This can be spoofed by other users for them to find that users data.
Lastly if you are doing a find on the user object, I assume you might be storing data there. This is generally bad practice. If you need to store user data with their profile, it's best to create a new collection and publish/subscribe what you need.

Meteor: Reactive update, cascaded delete/update. Normalizing vs Denormalizing

How to do cascaded delete, update and reactive update in existing joined documents? Say for example I join Posts collection with Meteor.users collection with userId() as author. I could do transform function on the Posts collection to get user data of the author, like username and display username of the author on any post. The problem is when user changes his/her username, existing posts won't update author's username reactively. And when you delete parent document child documents are still there. I used popular smart packages like publish-composite and collection-helpers but problem still exists. Any expert meteor dev can help me on this? Thank you.
If you want to use collection-hooks to solve this problem, the following pseudo code should get you going:
// run only on the server where we have access to the complete dataset
if (Meteor.isServer) {
Meteor.users.after.update(function (userId, doc, fieldNames, modifier, options) {
var oldUsername = this.previous.username;
var newUsername = doc.username;
// don't bother running this hook if username has not changed
if (oldUsername !== newUsername) {
Posts.update({
// find the user and make sure you don't overselect those that have already been updated
author: userId,
authorUsername: oldUsername
}, {$set: {
// set the new username
authorUsername: newUsername
}}, {
// update all documents that match
multi: true
})
}
}, {fetchPrevious: true});
}

Storing per-user data in Meteor

I want to store information for every logged in user in my meteor app, such as their profile picture, bio, ect. But if I try to do something like Meteor.user().picLink = "..."; it appears to get erased on every subsequent call to Meteor.user(). I assume this means I'm not supposed to store extra data directly on the user object like that.
The only response to that that I can think of is to have a separate collection with user data in it. But that seems like it would be hard to keep consistent with Meteor.users. Is there a better way?
All user accounts come with an automatically published profile field which you can update like so:
var userId = Meteor.userId();
var url = 'http://example.com/kittens.jpg';
Meteor.users.update(userId, {$set: {'profile.photo': url});
That will update the underlying database and persist across connections.
As I point out here you should be aware that the profile object is currently editable by default even when the insecure package has been removed. This means any user can open up the console and modify his/her profile.
A better approach is to deny the updates and to use a method instead:
client
var url = 'http://example.com/kittens.jpg';
Meteor.call('update.photo', url);
server
Meteor.users.deny({
update: function() {return true;}
});
Meteor.methods({
'update.photo': function(url) {
check(url, String);
Meteor.users.update(this.userId, {$set: {'profile.photo': url}});
}
});

Resources