Routing to a specific user profile using iron router (METEOR) - javascript

As the question says, I need to create a specific route for every user that I have. In my case employers. Now all the examples on the web are connected with the USERS collection.
In my case I want to route to: "/employer/:_id" but I have the Employer ID in the Collection Employers. So basically I have to get the Employer ID via the key from User ID
Im kinda stuck on returning the Employer ID value to the route...
METHODS.js
getEmployerId: function(currentuser){
employerId = Employer.find({"user":currentuser}).fetch();
return employerId;
}
ROUTER.js
Router.route("/employer/:_id", {
name:"employer",
template:"employer",
layoutTemplate:'employerLayout',
data: function(){
var currentuser = Meteor.userId();
Meteor.call("getEmployerId", currentuser, function(error, result){
if(error){
console.log("error", error);
}
if(result){
return true; // I belive here is where I have to pass it up to the ROUTE
}
});
},
onBeforeAction:function(){
var user = Meteor.userId();
if(!user || !Roles.userIsInRole(user, ['employer'])) {
Router.go("verification");
}else {
this.next();
}
return true;
},
});
And this is how my Employer collection looks like:
meteor:PRIMARY> db.employer.find().pretty()
{
"_id" : "qCFGZa4ogc5LR56PL", // I need this for the route
"createdAt" : ISODate("2015-07-18T13:19:16.098Z"),
"user" : "owfJ4ozrfsp26o8G4" // the key through which i can return the ID, got it from the user session?
}
Anyone has a suggestion how to do this? And is this a good approach for each user(employer) profile? Any tutorial, example or anything that describes an application with user profiles would be much appriciated!

Ok, looks like you're nearly there.
I don't think you want the ::id parameter. You send the user to simply /employer, where he does the sign in, so you have his user id.
Then change getEmployerId to getEmployer: in other words, get the whole of the employer record.
getEmployer: function(currentuser){
return Employer.find({"user":currentuser}).fetch();
}
Then in your data: function of the router instead of returning true you return the record you find. In this way the record is available for your template (that's what the data function is for)
data: function(){
var currentuser = Meteor.userId();
Meteor.call("getEmployer", currentuser, function(error, result){
if(error){
console.log("error", error);
}
if(result){
return result;
}
});
},

Related

MeteorJS Infinite loop when using meteor call and meteor method

I have a sample code that goes like this:
Client Helper:
getUsername: function (userId) {
Meteor.call("getUsername", userId, function (err, result) {
if(!err) {
Session.set("setUsername", result);
else {
console.log(err);
}
});
return Session.get("setUsername");
}
Server
Meteor.methods({
"getUsername": function (userId) {
var x = Meteor.users.find({_id: userId}, {fields: {username:1}}).fetch()[0];
return x.username;
}
});
The result of this code is an infinite loop of username passing to the client. Is there a way to stop the loop and pass only the data that is needed on the client? I believe the reactivity is causing the data to loop infinitely and I am not sure how to stop it. I tried using "reactive":false on my query in the server but it does not work.
If you want to access username everywhere in client templates (so thats why you put it into session), I would not set it in template helper. I would set it on startup and get username from session in template helpers (without calling server method)
If you need username just in one template, so you want to return its value from your template helper, do not put it into session, just return it in your server method callback.
Based on your sample code, I assume, you have a set of posts and you are retrieving user name based on user id for each post. Then instead of doing it this way, you should use publish composite package to publish related users as well.
Meteor.publishComposite('getPosts', function (postIds) {
return [{
find: function() {
return Posts.find({ _id: { $in: postIds }});
// you can also do -> return Posts.find();
// or -> return Posts.find({ /* or what ever your selector is to get the posts you need*/ });
},
children: [{
find: function(post) {
return Meteor.users.find({
id: post.userId //or the correct field in your post document to get user id
}, {
fields: {
"profile": 1
}
});
}
}}
}]
});
This way your publication will take care of publishing related users along with posts. You don't need to use methods and call them each time.

Meteor user helper hangs off after update

I have this helper:
agreed: function (){
if (Meteor.users.findOne({_id: Meteor.userId(), profile: {agreedTermsOfUse: 'true'}}))
return true;
}
On the page where I check it I have this:
{{#unless agreed}}
agree form
{{else}}
Create item form.
{{list of item}}
{{/unless}}
So far, all goes well. The user signs up then he can create an item and it renders on the list of items..
Now, I've added another Meteor.call, which when getting the success call back on the client, for the creating item, it adds the item id to the users' profile.hasItems.
Then after getting succes for that method, "unless" returns false, and I have to submit the agree to form again.
What am I missing? Thanks.
"submit .create_restaurant": function (event) {
event.preventDefault();
var text = event.target.create_restaurant.value;
Meteor.call('CreateRest', Meteor.userId(), text, function(error, result){
if(error){
}else{
console.log(result, Meteor.userId());
Meteor.call('userRestaurants', result, Meteor.userId(), function (error, result) {
if (error) {
alert(123);
} else {
console.log(result);
}
})
}
}
);
event.target.create_restaurant.value = "";
}
methods:
'CreateRest': function(user_id, title) {
check(title, String);
check(user_id, String);
return callback = Restaurants.insert({
createdBy: user_id,
createdAt: new Date(),
title: title
});
},
'userRestaurants': function(rest_id, createdBy) {
var restId = checkHelper(rest_id, createdBy);
if(restId)
console.log(rest_id, createdBy);
{
var callback = Meteor.users.update(
createdBy,
{$addToSet: {'profile.hasRestaurants': restId}}
);
return callback;
}
}
I don't know why you're seeing the behaviour that you are, but I do know that you have other problems to sort out first :)
You have a huge security hole - you're passing the user id through to the method from the client. That means that anyone can simply open the browser console and create a restaurant with any user id they like as the owner. Instead, use this.userId in the method to get the id of the caller.
Why the round trip to the server? Just have the first method update the client.
So, something like this (untested, written by hand here):
"submit .create_restaurant": function (event) {
event.preventDefault();
var text = event.target.create_restaurant.value;
Meteor.call('CreateRest',text, function(error, result){
if(error){
alert(123);
}else{
console.log(result);
}
});
event.target.create_restaurant.value = "";
}
and:
'CreateRest': function(user_id, title) {
check(title, String);
check(this.userId, String);
userId = this.userId;
Restaurants.insert({
createdBy: userId,
createdAt: new Date(),
title: title
}, function(err, restId) {
if (err) throw new Meteor.Error(err);
Meteor.users.update(
userId,
{$addToSet: {'profile.hasRestaurants': restId}},
function (err, res) {
if (err) throw new Meteor.Error(err);
return restId;
}
);
});
Once that's implemented properly it might start working. If it doesn't then the issue isn't related to the code you're posting.
Finally note that from a schema perspective it's really odd that that you have profile.hasRestaurants. To find the restaurants that a user has you should just do a find on the Restaurants collection.

Meteor publish: admin can view all data, user only his data

I have a collection (Collection2 package):
tickets = new Mongo.Collection("tickets");
I would like to show, through the template, ALL USER tickets if admin is logged in, else only current user tickets.
My server code (meteor-rules package) :
Meteor.publish('tickets', function(){
if (Roles.userIsInRole(this.userId, ['admin'])){
return tickets.find({},{sort:{deadline: 1}});
}else{
return tickets.find({_id: this.userId}, {sort:{deadline: 1}});
}
});
My client code:
Template.dashboard.helpers({
tickets: function () {
Meteor.subscribe('tickets');
});
Nothing happened on template...what did I do wrong?
Your template helper must return actual data, but in your code you don't return anything, just subscribe to your tickets.
Template.dashboard.helpers({
tickets: function () {
// return a cursor fetching all tickets that were pushed to the client
// thanks to the subscription
return tickets.find();
}
});
Depending on your needs, you may want to use iron:router to display your dashboard only when the subscription is ready, or else it will be displayed unpopulated at first.

meteor users not synchronize published sub fields of profile

Working on my social app I've found a strange behavior in the collection Meteor.users, this problem does not occur with other Collections using the same methodologies
I would like to have an initial list of users downloading a minimum number of information for everyone and when I open the panel to a specific user I subscribe a different showing more information if the specified user is a friend of mine.
But after subscribe the client collection Meteor.users is not updated!
CLIENT
Meteor.startup(function() {
Meteor.subscribe('usersByIds', Meteor.user().profile.friends, function() {
//... make users list panel using minimal fields
});
//performed when click on a user
function userLoadInfo(userId) {
Meteor.subscribe('userById', userId, function() {
var userProfile = Meteor.users.findOne(userId).profile;
//...
//make template user panel using full or minimal user fields
//...
//BUT NOT WORK!
//HERE Meteor.users.findOne(userId) keep minial user fields!!
//then if userId is my friend!
});
}
});
SERVER
//return minimal user fields
getUsersByIds = function(usersIds) {
return Meteor.users.find({_id: {$in: usersIds} },
{
fields: {
'profile.username':1,
'profile.avatar_url':1
}
});
};
//return all user fields
getFriendById = function(userId) {
return Meteor.users.find({_id: userId},
{
fields: {
'profile.username':1,
'profile.avatar_url':1
//ADDITIONAL FIELDS
'profile.online':1,
'profile.favorites':1,
'profile.friends':1
}
});
};
//Publish all users, with minimal fields
Meteor.publish('usersByIds', function(userId) {
if(!this.userId) return null;
return getUsersByIds( [userId] );
});
//Publish user, IF IS FRIEND full fields
Meteor.publish('userById', function(userId) {
if(!this.userId) return null;
var userCur = getFriendById(userId),
userProfile = userCur.fetch()[0].profile;
if(userProfile.friends.indexOf(this.userId) != -1) //I'm in his friends list
{
console.log('userdById IS FRIEND');
return userCur; //all fields
}
else
return getUsersByIds( [userId] ); //minimal fields
});
This is a limitation or bug in DDP. See this.
A workaround is to move data out of users.profile.
Like this:
//limited publish
Meteor.publish( 'basicData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find({_id: reqId },{
fields: { 'profile.username':1,'profile.avatar_url':1}
});
}
else {
this.ready();
}
});
//friend Publish
Meteor.publish( 'friendData', function( reqId ){
if ( this.userId ) {
return Meteor.users.find( {_id: reqId, 'friendProfile.friends': this.userId }, {
fields: {
'friendProfile.online':1,
'friendProfile.favorites':1,
'friendProfile.friends':1
}
});
}
else {
this.ready();
}
});
//example user
var someUser = {
_id: "abcd",
profile: {
username: "abcd",
avatar_url: "http://pic.jpg"
},
friendProfile: {
friends: ['bcde', 'cdef' ],
online: true,
favorites: ['stuff', 'otherStuff' ]
}
}
As given in a comment, this link reveals your problem. The current DDP Protocol does not allow publishing of subdocuments. One way to get around this is to create a separate collection with your data but a better way would probably to just remove some of the data and make it a direct object off of your user.
The best way to do this is add the data to your user's profile upon insert and then in the onCreateUser move the data onto the user directly:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
if (options.profile.publicData) {
user.publicData = options.profile.publicData;
delete options.profile.publicData;
}
user.profile = options.profile;
}
return user;
});
If you are allowing clients to perform user inserts make sure you validate the data better though. This way you can have the online, favorites, and friends in the profile and publish that specifically when you want it. You can then have username and avatar_url in the publicData object directly on the user and just always publish all-the-time.

How to grab data from a js script in Jade for Node.js?

I am trying to grab client side info that was queried already for the server side of a POST. Currently working on a friend button that when clicked POST's to a route file, but I am not sure how to grab the user.id associated with it and send it back to Node.JS.
Here is the jade file includes a for loop that has the user.id of each person.
extends layout
block content
div
legend Search Results
div#userResults
for user in ufirstName
a(href='/user/#{user.id}')
p #{user.firstName} #{user.lastName}
button.addContact Add Contact
Here is the route file: I am trying to figure out what to put on the other side of the friendRequest:
exports.addContactPost = function(req, res, err) {
User.findByIdAndUpdate(req.signedCookies.userid,{
friendRequest: req.body.friendRequest
}, function(err) {
if(err) {
console.log("post2");
return console.log('error');
//return res.render('addContactError', {title: 'Weblio'});
}
else {
console.log('postsuccess');
alert('Contact added');
}
});
};
This is the script file trying to do the magic:
//Add friends
$('.addContact').click(function() {
$.post('/addContact',
{friendRequest: $(this).data('user')});
if($(this).html!=='Contact Requested') {
return $(this).html('Contact Requested');
}
});
I would store the ID in the Add Contact link:
button.addContact(data-user=user.id) Add Contact
Amnd get it in the handler
$('.addContact').click(function() {
$.post('/addContact',
{
friendRequest: $(this).data('user');
}
// ...
}
Try this:
button.addContact(onclick="addContact('#{user.id}')") Add Contact
JS:
function addContact(userId){
$.post('/addContact', { friendRequest: userId }, function(result){
// use result
});
}

Categories