Meteor Publishing partial collection - meteor

I want to publish a collection to the user that the user 'owns'
Meteor.publish('test', function(){ return questiondummy.find({'author':'Jaspermid'});
});
And the subscribe to it with
Meteor.subscribe("test");
Unfortunately this does not work. I can find the collection within mongodb database with the exact username
{ "questionnumber" : 10, "user" : "Jaspermid", "timestamp" : 1407063133137, "answerarray" : [ "1", "2", "3", "4" ], "_id" : "fDcfnenhNsZNAmsze" }
1. Does anyone know why I cannot see this collection on the client side
2. How and where to incorporate (Meteor.user().username to publish to the current user only?
Thank you in advance

You have access to the current user's _id from within the publish function through this.userId. You can use that to find the username.
http://docs.meteor.com/#publish_userId
Meteor.publish('test', function(){
var user = Meteor.users.findOne(this.userId);
if (user)
return questiondummy.find({user: user.username});
this.ready();
});
this.ready() tells the client that the subscription is ready even though you haven't sent any data (as in when the user is not logged in).

The selector to find should be {'user':'Jaspermid'}, and not {'author':'Jaspermid'}.
Instead of the selector {'user':'Jaspermid'}, use {'user': Meteor.user().username}
EDIT
Oups, Meteor.user() can be used "Anywhere but publish functions". My mistake.

Related

How can I get other users' profiles details in meteor

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: how to access another user's details?

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

Reactive subscription on user collection

I am trying to subscribe to profdle information of a different user than the logged in user, but I am facing issues as mentioned below
I am using angular-material and my code looks like below:
//publish user info upon following user
Meteor.publish("getUserInfo", function (userId) {
return (Meteor.users.find({_id: userId}, {fields: {profile: 1}}));
});
//subscribe
$scope.$meteorSubscribe("getUserInfo", askLikeController.$root.askLike[0].userId).then(function (subscriptionHandle) {
//Second element in the userProfile array will have the profile of required user
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
});
Issues:
1. In the variable askLikeController.$root.usersProfile, I am getting both the loggedIn user and the desired userinfo having userId, I was expecting userinfo of only desired userId, why is this?
2. The subscription "getUserInfo" is not reactive, and even the subscription is lost after processing few blocks of code and then in the askLikeController.$root.usersProfile I am left with only user profile of logged in user, my guess is that my subscription is being replaced by inbuilt Meteor subscription for user.
How do I solve the issues?
Regards,
Chidan
First, make sure you have removed autopublish:
> meteor remove autopublish
To get reactivity in angular-meteor you need $meteor.autorun and $scope.getReactively. Here's an example:
// we need the requested id in a scope variable
// anytime the scope var changes, $scope.getReactively will
// ... react!
$scope.reqId = askLikeController.$root.askLike[0].userId;
$meteor.autorun($scope, function() {
$scope.$meteorSubscribe('getUserInfo', $scope.getReactively('reqId')));
}).then(function(){
askLikeController.$root.usersProfile = $meteor.collection(Meteor.users, false);
})
Getting only the user you selected: NOTICE- the logged in users is always published. So you need to specify which user you want to look at on the client side, just like you did on the publish method. So, in the subscribe method:
askLikeController.$root.usersProfile = $meteor.collection(function() {
return Meteor.Users.find({_id: $scope.getReactively('reqId')})
}, false);
At this point you might be better off changing it to an object rather than a collection:
askLikeController.$root.usersProfile = $scope.$meteorObject(Meteor.Users, {_id: $scope.getReactively('reqId')});

Refreshing page with meteor iron router

Here is the problem :
I am currently programming a chatapp based on what i found on github (https://github.com/sasikanth513/chatDemo)
I am refactoring it with iron-router.
When I go to the page (clicking on the link) I get an existing chatroom (that's what I want)
When I refresh the page (F5) I get a new created chatroom ! (what i want is getting the existing chatroom ...)
Here is the code in ironrouter :
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Session.get('currentId'); //id of the other person
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
You can find my github repo with the whole project : https://github.com/balibou/textr
Thanx a lot !
Your route data depends on Session variables which will be erased after a refresh. You have a few options but the easiest would be to put the room id directly into the route: '/chatroom/:_id'. Then you can use this.params._id to fetch the appropriate ChatRooms document. Note that you could still keep '/chatroom' for cases where the room doesn't exist, however you'd need to redirect to '/chatroom/:_id' after the insert.
In meteor, the Session object is empty when the client starts, and loading/refreshing the page via HTTP "restarts" the client. To deal with this issue, you could persist the user's correspondent id in a Meteor.user attribute, so that you could easily do:
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Meteor.user().profile.correspondentId;
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
This would work, with the proper permissions, but I would recommend not allowing the direct update of that value on the client (I don't know if you want users to be able to override their correspondentId). So if you want to secure this process, replace all that code with a server method call, where your updates are safer.
Another (and more common case) solution was given by David Weldon, if you don't mind having ids in your URL (and therefore not a single url)

Meteor: what is the right way to add custom settings object to users collection?

There are multiple examples on publish/subscribe but not clear on what is the best practice for storing custom data in the in-built "users" collection in Meteor (especially in the new possibility of template specific collections).
For example, I need to store user browse history - something that is accessible through Meteor.user().settings.history.lastvisited[]
The challenge is:
Is any special publish / subscribe required for the above? (the
reason being, I am assuming the users collection is already
published and available on client side - so do we need another?)
How to take care of edge cases where user is new and hence settings.history object may not be defined? Can we have a special publish that automatically takes care of creating an empty object if the settings is undefined? How to do it?
I did this :
// server side
Meteor.publish('userSettings', function (maxRows) {
if (this.userId) {
return Meteor.users.find({ _id: this.userId }, { fields: {'settings':1}});
}
this.ready();
});
//client side
Meteor.subscribe('userSettings');
But I do not see anyway how I can access the published "userSettings" object on the client side - what is missing ??
You can create a field and set it to false/'', on each user you create using the accountsOnCreateUser method.
Accounts.onCreateUser(function(options, user) {
//this function gets called each time a user has been created on the Meteor.user collection
if (options.profile)
user.settings = ''; //this is just and example.
return user;
})
Now the publish looks ok, but in order to get it work im always use a Tracker.autorun function.
Tracker.autorun(function(){
Meteor.subscribe('userSettings');
})
Why the autorun? well if you don't call the auto run here, the subscription get only called 1 time when the apps loads, and not when the user documents.
Take care of yours deny/allow permissions, check this meteor:common mistakes post on the Profile editing section
Also the subscribe function have a callback function. Meteor.subscribe(name, [arg1, arg2...], [callbacks]), so you can do something like this.
var myUserSubscription = Meteor.subscribe('userSettings',function(){
console.log("ok im here on the client side")
console.log("this user subscription is ready " + myUserSubscription.ready())
})
console.log("outside the subscription why not? " + myUserSubscription.ready();
About ready();
True if the server has marked the subscription as ready. A reactive
data source.

Resources