So, I have bunch of collections joined with Meteor.users collection by userId as foreign key. E.g. I have Posts collection and each post has its own page. On a post page I have userId as author and a bunch of comments(from Comments collection) on that post each has postId and userId. UserIds are ugly, instead I would like to display usernames. How would you achieve that? Any smart package that I can use or working example? I don't like the idea of publishing all users data. Thank you.
You don't need to publish all data. In publish method you can use fields specifier in that way:
Meteor.users.find({}, {fields: {'username': 1}});
That way you have only _id and username published.
Then in meteor helper you can have:
username: function() {
var user = Meteor.user.findOne(this.userId);
return user.username || user.profile.name;
}
if Post is your context
Related
I want to publish all the users to the client (which will eventually be only the 'profile' field of all the users):
The publication on the server looks like this:
Meteor.publish('users', function users() {
return Meteor.users.find();
});
In my template I then have a subscription that looks like this:
Template.Users_show_page.onCreated(function usersShowPageCreated() {
this.subscribe('users');
});
However the 'users' variable is not available and I still have to access the users via Meteor.users, such as in the following code:
Template.Users_show_page.helpers({
users() {
return Meteor.users.find();
}
});
Why is this?
I think I need to create a client-side collection with my choice of name - i.e. 'users', and then I can access that collection.
Where do I do this and how do I make that sync with the users in the database?
MyCollection = new Meteor.Collection('foo');
Meteor.publish('myPublication', function() {
return MyCollection.find();
});
The code above doesn't create a myPublication variable anywhere. It's just the name of the publication/subscription. You can even have multiple different subscriptions over the same collection. This code returns a cursor for the foo Mongo collection, which you access via MyCollection object.
So, your code doesn't need to create a new Mongo.Collection. Just use Meteor.users because that's the Mongo.Collection object that's already linked to "users" in MongoDB.
If you really want to access the documents in a users variable, you still need to create the helper as you suggested, although it's better you just use Meteor.users instead of the helper below:
Template.template_name.helpers({
users: function(){ return Meteor.users.find() }
});
I have got problem accessing the user profile details of the users other then the current user.
The goal is to display a little footer under a each of the posts in the kind of blog entries list . Footer should consist of the post and author details (like date, username etc.).
Blog entry is identified by authors' _id but the point is that I can not access
Meteor.users.find({_id : authorId});
Resulting cursor seems to be the same as Meteor.user (not 'users') and consists of one only document, and is valid for the current user ID only. For others, like authors ID, I can only get an empty collection.
The question is, if is there any way, other then next Meteor.users subscription to get authors profile (like username profile.nick etc) ???
Update: You can Publish Composite package if you want to get blog entry and user details in a single subscription. See the following sample code and edit as per your collection schemas,
Meteor.publishComposite('blogEntries', function (blogEntryIds) {
return [{
find: function() {
return BlogEntries.find({ courseId: { $in: blogEntryIds }});
// you can also do -> return BlogEntries.find();
// or -> return BlogEntries.find({ courseId: blogEntryId });
},
children: [{
find: function(blogEntry) {
return Meteor.users.find({
id: blogEntry.authorId
}, {
fields: {
"profile": 1,
"emails": 1
}
});
}
}}
}]
});
End of update
You need to publish Meteor.users from the server to be able to use it on client. accounts package will publish current user, that's why you are only seeing current user's information.
In a file in server folder or in Meteor.isServer if block do something like this
//authorIds = ["authorId1", "authorId2];
Meteor.publish('authors', function (authorIds) {
return Meteor.users.find({ _id : { $in: authorIds }});
});
or
Meteor.publish('author', function (authorId) {
return Meteor.users.find({ _id : authorId });
});
Then on client side subscribe to this publication, in template's onCreated function, with something like this
Meteor.subscribe('author', authorId); //or Meteor.subscribe('author', authorIds);
or
template.subscribe('author', authorId); //or template.subscribe('author', authorIds);
If you want to show only username (or a few other fields), you can save them in post document along with authorId. For example:
post:{
...
authorId: someValue,
authorName: someValue
}
You can use them in your templates as a field of a post.
If you have too many fields which you do not want to embed in post document, (so you want to keep only authorId), you can use publish-composite when you make your posts publication. (See example 1)
You do not need to publish all your users and their profiles.
Meteor.users.findOne() gives me back my user document.
Meteor.users.findOne({_id: 'my ID'}) gives me back my user document.
Meteor.users.findOne({_id: 'another users's ID'}) gives me back UNDEFINED.
This is obviously restricted by security. But how can I access another users's account details e.g. _id, name, profile, etc?
You'll need to add a publisher for the user. Here's an example:
// The user fields we are willing to publish.
const USER_FIELDS = {
username: 1,
emails: 1,
};
Meteor.publish('singleUser', function (userId) {
// Make sure userId is a string.
check(userId, String);
// Publish a single user - make sure only allowed fields are sent.
return Meteor.users.find(userId, { fields: USER_FIELDS });
});
Then on the client you can subscribe like this:
Metor.subscribe('singleUser', userId);
or use a template subscription like this:
this.subscribe('singleUser', userId);
Security notes:
Always check the arguments to your publishers, or clients can do bad things like pass {} for userId. If you get an error, make sure you meteor add check.
Always use a fields option with the users collection. Otherwise you'll publish all of their secrets. See the "Published Secrets" section of common mistakes.
Run it on the server like so:
Server:
Meteor.publish("otherUsers", function (userID) {
return Meteor.users.findOne({_id: userID});
});
Client:
Meteor.subscribe("otherUsers", <userIdYouWantToGetDetailsFor>);
Then you can just do a Meteor.users.findOne on the client keep in mind you can only do it for your user and the userID that you passed in the meteor subscribe
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});
}
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);
});