Rendering user profile page in MeteorJS - javascript

I'm trying to create a page for users so that when they are logged in they can see the content they've uploaded onto the site. I can't seem to get the page to render. I've searched all over, but can't seem to find anything. Any ideas of where I'm going wrong?
from my publication.js file
Meteor.publish('myTickets', function() {
var currentUserId = this.userId;
return Tickets.find({
createdBy: currentUserId
})
});
from the router.js
Router.route('/users/:_id', {
name: 'userPage',
waitOn: function() {
return Meteor.subscribe('myTickets');
},
data: function() {
return Tickets.find({
userId: this.userId
})
}
});
userpage.js
Template.userPage.helpers({
tickets: function() {
var currentUserId = Meteor.userId();
return Tickets.find({
createdBy: currentUserId
}, {
sort: {
createdAt: -1
}
});
}
});

I asked you in a comment what you exactly wanted to do (you have a route with a parameter, but you are never using it), but anyway, I think I got your problem;
You are using this.userId to get your current user ID in your router, but your should use Meteor.userId() instead; this.userId can only be used in publish methods.

Related

Meteor.user() is undefined in client-side controller

In the client-side of a METEORJS application, i have a controller that display some users.
I have a problem with Meteor.user() function, the error is : Meteor.user(...) is undefined.
Here is my code :
this.AdminUsersController = RouteController.extend({
template: "Admin",
yieldTemplates: {
'AdminUsers': { to: 'AdminSubcontent'}
},
onBeforeAction: function() {
var permissions = Meteor.user().profile.permissions;
if (permissions && permissions.indexOf('Users') != -1)
this.next();
else this.redirect('/admin/dashboard');
},
action: function() {
if(this.isReady()) { this.render(); } else { this.render("Admin"); this.render("loading", { to: "AdminSubcontent" });}
/*ACTION_FUNCTION*/
},
isReady: function() {
var subs = [
Meteor.subscribe("users")
];
var ready = true;
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
var data = {
params: this.params || {},
users: Users.find({labo_id: Meteor.user().profile.labo_id}, {sort: {createdAt:-1}})
};
return data;
},
onAfterAction: function() {
}});
It's in the data function.
I try to retrieve all users that are connected to the logged in user and got the same labo_id field...
I don't know why it give me that, because in the onBeforeAction function, i can access to Meteor.user(), and specially his profile...
Someone know what can i do to make it run ?
Thanks for your future answers :)
This is a timing issue. Meteor.user() does not necessarily return data, if it hasn't been loaded yet. Meteor.userId() however will return the _id of the user record (if they are logged in). If you can change your query to rely on that _id, then it can work.
Depending on which router you are using, you can add a resolve: entry to ensure that the route waits for the user record to be loaded before activating it, and then your query will work.

Collection helper throws a not defined error

Im trying to set up a list of "rooms". The intended sequence:
Click name of the user on his/her profile page
Check for existing room. If yes, go to that room, if not set up new room.
Im using both dburles:collection-helpers and reywood:publish-composite.
Its throwing me this error.
TypeError: Cannot read property 'username' of undefined
at Document.Rooms.helpers.recName (rooms.js:18)
And line 18 is:
return Meteor.users.findOne({ _id: this.receiver }).username;
i.e. _id: this.receiver is undefined.
I also tried to add protective checks in the collection helpers but error remains. I.e. return user && user.username for example.
One thing I note is that, I noticed when I click on the user, it goes to the room linked to the user's id. However when I click back, it jumps to a blank room with a different id thats unrecognised.
The relevant codes:
Server publish
Meteor.publish("onlusers", function (){
return Meteor.users.find({});
});
Rooms.js collection helper
Rooms.helpers({
recName: function() {
return Meteor.users.findOne({ _id: this.receiver }).username;
}
});
User.js (for profile page events)
Template.usersShow.events({
'click .user': function() {
var receiver = this._id;
Session.set('chatId', this._id);
var res = Rooms.findOne({
$or: [
{ owner : this._id },
{ receiver : this._id }
]
});
if(res){
Router.go('roomDetail', { "_id" : res._id });
} else {
var newRoom = Rooms.insert({
owner : Meteor.userId(),
receiver : receiver,
username : Meteor.user().username,
});
Session.set('roomid', newRoom);
Router.go('roomDetail', { "_id" : newRoom });
}
}
});
Your diagnosis:
_id: this.receiver is undefined.
May be misleading. What is also possible is that the user subscription isn't completely loaded when your helper runs. I was helping someone else with a similar problem with publish-composite the other day - the subscription is marked as ready when the parents are ready but the children may not have finished loading yet. I think of this as a bug in publish-composite, all the related objects really need to be there before the subscription can be marked as ready.
Instead of returning:
return Meteor.users.findOne({ _id: this.receiver }).username;
You can do:
var user = Meteor.users.findOne({ _id: this.receiver });
return user && user.username;
So you'll get nothing back until the user object loads but you won't throw an error.

Meteor Routing, Pub/Sub

I'm trying to make a publishment statement to publish
ONLY the author(OP)'s profile avatar. I am thinking of grabbing the _id of the page. And from that page, I will grab the userId which is the author's _id and try to show the profile.
However, I have been very unsuccessful, and currently, I am using the following. Publishing EVERY user's profile avatar.
Publications.js
//Need to filter this to show only OP.
Meteor.publish("userPostAvatar", function() {
return Meteor.users.find( {} ,
{
fields: {'profile.avatar': 1}
})
});
Meteor.publish('singlePost', function(id) {
check(id, String);
return Posts.find(id);
});
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
waitOn: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('userPostAvatar')
];
},
data: function() {
return Posts.findOne({_id:this.params._id});
}
});
You can do a simple join in the userPostAvatar publish function like this:
Meteor.publish('userPostAvatar', function(postId) {
check(postId, String);
var post = Posts.findOne(postId);
return Meteor.users.find(post.authorId, {fields: {profile: 1}});
});
This assumes posts have an authorId field - adjust as needed for your use case. Note three important things:
You will need to subscribe with this.params._id just as you did for singlePost.
The join is non-reactive. If the author changes, the avatar will not be republished. Given the general nature of posts I assume this isn't a problem.
I didn't publish the nested field profile.avatar on purpose because doing so can cause weird behavior on the client. See this question for more details.
I believe you can achieve this within the iron:router data context, by finding the post, associated author (whatever the field is), and then the subsequent user avatar. You can return an object to the iron:router data context. Then you can access post and avatar in the template as variables (so you might need to adjust the template output a little).
Publications.js
Meteor.publish("userPostAvatar", function() {
return Meteor.users.findOne( {} ,
{
fields: {'profile.avatar': 1}
})
});
Meteor.publish('singlePost', function(id) {
check(id, String);
return Posts.find(id);
});
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
waitOn: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('userPostAvatar')
];
},
data: function() {
var post = Posts.findOne({_id: this.params._id});
var avatar = Users.findOne(post.authorId).profile.avatar;
return {
post: post,
avatar: avatar
};
}
});
Two problems with this method are that you could achieve the same thing with template helpers, and the user publication hasn't been limited to one user (I'm unsure how to do this unless we know the authorId within the waitOn, although maybe you could try moving the logic to there instead of the data context as my example shows).

Username not changing properly using MongoDB update function?

Below is a snippet of my code where I begin by searching the collection of notes to see if any of them contain the username that I am changing my current session's username to. If the username has not yet been used, it may be changed to that so I change the current session's username and then I update every note to be under this new username then display a changesuccess.jade file. However, when I run the code everything appears to run fine exept the username for each note doesn't change. I feel like it's due to the find() method on the 5th line. Any help would be greatly appreciated!
router.post('/changeusername',function(req, res) {
var newUsername = req.body.username;
var user = req.session.username;
var userFound = notesCollection.find( { owner: newUsername } )
var results = function(infoInput) {
res.render("changedUser.jade", {title: "Username Change",
info: infoInput});
}
var checkChange = function(err) {
if (err) {
results("Username change failed!");
} else {
results("Username changed!");
}
}
console.log(userFound);
if (!userFound.length) {
notesCollection.update({ owner: user },
{ owner: newUsername},
{ multi: true },
checkChange);
} else {
res.render("changedUser.jade", {title: "Username Change",
info: "Username change failed!"});
}
});
If i understand your problem correctly, you are trying to update a collection in mongodb and it is not getting updated.
So the problem is with the way you are calling mongoose#update.
Since you want to update 'owner', you should use mongodb#$set
Mongoose supports the same $set operator in the conditions too.
So just a little correction for your case:
var conditions = { owner: user }
, update = { $set: { owner: newUsername }}
, options = { multi: true };
notesCollection.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
})
So try this now.

Sails.js Set model value with value from Callback

i need to provide something like an association in my Model. So I have a Model called Posts with an userid and want to get the username from this username and display it.
So my ForumPosts.js Model looks like the following:
module.exports = {
schema: true,
attributes: {
content: {
type: 'text',
required: true
},
forumTopicId: {
type: 'text',
required: true
},
userId: {
type: 'integer',
required: true
},
getUsername: function(){
User.findOne(this.userId, function foundUser(err, user) {
var username = user.username;
});
console.log(username);
return username;
}
}
};
I know that this return will not work because it is asynchronus... But how can i display it in my view? At the Moment i retrive the value with:
<%= forumPost.getUsername() %>
And for sure get an undefined return...
So the question is: How can I return this value - or is there a better solution than an instanced Model?
Thanks in advance!
Off the top of my head, you can just load associated user asynchronously before rendering:
loadUser: function(done){
var that = this;
User.findOne(this.userId, function foundUser(err, user) {
if ((err)||(!user))
return done(err);
that.user = user;
done(null);
});
}
then in your controller action:
module.exports = {
index: function(req, res) {
// Something yours…
forumPost.loadUser(function(err) {
if (err)
return res.send(err, 500);
return res.view({forumPost: forumPost});
});
}
}
and in your view:
<%= forumPost.user.username %>
This is kind of a quick and dirty way. For a more solid and long-term solution (which is still in development so far) you can check out the alpha of Sails v0.10.0 with the Associations API.
So this particularly case of associations between your models. So here you have a User model and ForumPost model and you need the user object in place of your user_id as user_id works as a relationship mapping field to your User model.
So if your are using sails V0.9.8 or below you need to handle this logic in your controller where ever you want to access User model attributes in your view.
In your controller write your logic as:
model.export = {
//your getForumPosts method
getForumPosts : function(req,res){
var filters = {};
forumPost.find(filters).done(function(err,posts){
if(err) return res.send(500,err);
// Considering only one post obj
posts = posts[0];
postByUser(posts.user_id,function(obj){
if(obj.status)
{
posts.user = obj.msg;
delete posts.user_id;
res.view({post:posts});
}
else
{
res.send(500,obj.msg);
}
});
}
}
}
function postByUser(user_id,cb){
User.findOne(user_id).done(function(err,user){
if(err) return cb({status:false, msg:err});
if(user){
cb({status:true, msg:user});
}
}
}
and then you can access your post object in your view.
Or else you can keep watch (at GitHub) on next version of sails as they have announced associations in V0.10 n it is in beta testing phase as if now.

Categories

Resources