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

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.

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.

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

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;
}
});
},

Meteor publication and subscription not working

I am working on an admin page that shows a list of the user's orders. For some reason, publication function is not receiving the user's ID as a parameter from my subscription function.
My user object contains the attributes:
_id
userId
confirmed
My orders object contains the attributes:
_id
userId
amount
date
My publication:
Meteor.publish('clientOrders', function(userId) {
console.log('user is ' + userId);
return Order.find({_id: userId});
});
My subscription:
viewClientOrders = RouteController.extend({
loadingTemplate: 'loading',
waitOn: function () {
Meteor.subscribe('clientOrders', this.params._id);
},
action: function() {
this.render('viewClientOrders');
}
});
My route:
Router.route('/viewClientOrders/:id', {name: 'viewClientOrders', controller: 'viewClientOrders', onBeforeAction: requireLogin});
I did a console.log for the userId and it returns null, although I clearly passed a userId parameter (this.params._id) in my subscription function. However, if I pass a parameter of 3, for testing purposes, it will work. Also, I used a similar publication/subscription method to view a client's profile, passing in the exact same parameters and it works fine...
Anyone know what's going on? Thanks!
Make sure you return in your waitOn. Iron Router will only proceed to render if it knows the subscription is ready:
viewClientOrders = RouteController.extend({
waitOn: function () {
return Meteor.subscribe('clientOrders', this.params._id);
}
...
Make sure you've also set a loadingTemplate (docs).

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.

Meteor Publish Returning Empty Cursor

I'm using Backbone.js to route profile views so I can view data belonging to /user, and that part works fine. I'm able to generate an _id based on the username and pass it into the server publish function, which logs it. However, when I log the results back to the client in the subscribe function, my result looks like this:
Object {stop: function, ready: function}
//Client Side
Template.userquery.userproject = function() {
var query = Session.get('userquery');
var user = Meteor.users.findOne({username: query});
if (user) {
console.log(user._id); //(works)
campaigns = Meteor.subscribe('userquery', user._id, function() {
console.log('ready'); //(works)
});
console.log(campaigns); //(returns Object {stop: function, ready: function})
return campaigns;
}
}
//Server Side
Meteor.publish('userquery', function(userid) {
console.log('break');
console.log(userid); //(I get userid in Terminal)
var campaigns = Campaigns.find({owner: userid}, {fields: {owner: 1, name: 1}});
if (campaigns) {
console.log(campaigns);
return campaigns;
}
});
Am I missing something in this function? I have autopublish turned off because it was generating my search twice.
Meteor.subscribe, according to the docs, "Returns a handle that provides stop() and ready() methods." So the behaviour you're seeing is intended.

Categories