I have a Meteor application with a publish of:
Meteor.publish('my_items', function() {
var selector = {owner_id: this.userId};
var items = ItemOwnership.find(selector, {fields: {item_id: 1}}).fetch();
var itemIds = _.pluck(items, 'item_id');
return Items.find({
_id: {$in: itemIds},
item_archived_ts: { $exists: false }
});
});
and a subscription of this:
Meteor.subscribe('my_items');
The application allows for the user to add items to the 'Items' collection and this is done by calling a server method. The Items collection on the server is updated with the new record, but the client-side equivalent collection is not showing the new record. Is there anything obviously wrong with what I am doing, or some way to debug this?
p.s. there are no client/server-side errors occurring?
I found a way to accomplish this using the reywood:publish-composite Meteor package. Here is the publish that achieves this:
Meteor.publishComposite('my_items', {
find: function () {
var selector = {owner_id: this.userId};
return ItemOwnership.find(selector, {fields: {item_id: 1}});
},
children: [
{
find: function(IOrecord){
return Items.find({
_id: IOrecord.item_id,
item_archived_ts: { $exists: false }
});
}
}
]
});
Related
I'm trying to access the userIds stored in a collection and then use them to publish the details of all of the meteor.users. My publish function doesn't isn't return anything?
Meteor.publish('allUsersWithOffers', function () {
var user = Offers.find({}, {fields: {"UserId": 1}});
return Meteor.users.find({_id: user});
});
Give this a try:
Meteor.publish('allUsersWithOffers', function () {
var offers = Offers.find({}, { fields: { UserId: 1 } }).fetch();
var ids = _.pluck(offers, 'UserId');
// This is critical - you must limit the fields returned from
// the users collection! Update this as needed.
options = { fields: { username: 1, emails: 1 } };
return Meteor.users.find({ _id: { $in: ids } }, options);
});
find returns a cursor - you need to call fetch to actually get the documents.
I'm publishing this user information (as a test FYI, not going to reveal all this information in production)
Meteor.publish('user.private', function userPrivate() {
if (!this.userId) {
return this.ready();
}
return Meteor.users.find({
_id: this.userId
}, {
fields: {
'services.microsoft': 1,
'services.google': 1
}
});
});
I'm subscribing like this:
Template.App_body.onCreated(function appBodyOnCreated() {
this.subscribe('user.private');
});
I've confirmed that this exact query works and returns data using meteor mongo, yet this information does not appear in the Meteor.user() call on the client. What am I doing wrong?
try changing the publication name to null to make it automatically publish.
Meteor.publish(null, function () {
return Meteor.users.find(this.userId, {
fields: { /** ... fields here */ }
});
});
I'm currently have pub/sub method for users like this:
Meteor.subscribe("userData");
Meteor.publish('userData', function () {
return Meteor.users.find({}, {fields: {profile: 1}});
});
And this works fine. But I have problems if I want to add few fields in profile in observe callbacks, like this:
Meteor.publish('userData', function () {
var self = this;
var users = Meteor.users.find({}, {fields: {profile: 1}});
var subHandle = users.observe({
added: function (fields) {
fields.profile.postCount = Post.find({'owner': fields._id}).count();
self.added("userData", fields._id, fields);
},
changed: function(newObj, oldObj){
fields.profile.postCount = Post.find({'owner': fields._id}).count();
self.changed("userData", newObj._id, newObj);
},
removed: function(obj) {
self.removed("userData", obj._id);
}
});
self.ready();
self.onStop(function () {
subHandle.stop();
});
});
Problem is that when I try to find users on client side, I can find only one user, myself if I'm logged in (Meteor.users.find().fetch() inside console). And I saw that publication is finished (Network WS section inside console), so I know that more than one users is pushed to client side.
So I obviously doing something wrong, can someone point me to the right direction?
There may be more than one issue here, but what catches my eye is the fact that you are using the publication's name in your added/changed/removed callbacks instead of the collection name ('users'). Try modifying your observe as follows:
var subHandle = users.observe({
added: function (fields) {
fields.profile.postCount = Post.find({'owner': fields._id}).count();
self.added('users', fields._id, fields);
},
changed: function(newObj, oldObj){
fields.profile.postCount = Post.find({'owner': fields._id}).count();
self.changed('users', newObj._id, newObj);
},
removed: function(obj) {
self.removed('users', obj._id);
}
});
If that doesn't work, let me know and I'll examine it further.
I encounter an error using Meteor. I call an Method.method.
Template.WelcomeTemplate.events({
'click #btn-findgame': function(e) {
e.preventDefault();
console.log('clicked find game button');
Meteor.call('allocateGame', function(error, id) {
if (error) {
alert(error.reason);
} if (id) {
Router.go('gameRoom', {_id: id})
}
})
}
})
With my Method, I check if there is an room available, create one when the isn't otherwise join. And return the ID of this room.
Meteor.methods({
allocateGame: function () {
console.log('allocateGame method called')
var user = Meteor.user();
// find game where one player is in the room
var gameWaiting = Games.findOne({players: {$size: 1}})
if (!gameWaiting) {
console.log('no game available, create a new one');
var newGameId = Games.insert({players: [user._id], active: false, finished: false});
GameDetails.insert({gameId: newGameId, gameData: []});
return newGameId
} else {
if (_.contains(gameWaiting.players, user._id)) {
console.log('Cannot play against yourself sir')
} else {
console.log('Joining game');
Games.update({_id: gameWaiting._id}, {
$set: {active: true},
$push: {players: user._id}
});
return gameWaiting._id;
}
};
}
})
And my Router:
Router.map(function () {
this.route('welcome', {
path: '/',
controller: WelcomeController})
this.route('gameRoom', {
path: '/game/_:id'
})
});
The Error I recieve is:
Exception in delivering result of invoking 'allocateGame': TypeError: Cannot read property 'charAt' of null
at Object.IronLocation.set (http://localhost:3000/packages/iron-router.js?e9fac8016598ea034d4f30de5f0d356a9a24b6c5:1293:12)
And indeed, If I don't return an ID the Routing will continue as normal. However when I return an ID in my WelcomeTemplate an error will occur.
EDIT:
Even thought my MongoDB is updating my MiniMongo DB is empty. There must be a problem with syncing. Any idea where to look?
In the route, you set the path to be '/game/_:id', that is, a parameter with the name id. In your call to Router.go, you pass a parameter with the name _id.
Don't know if this solves your problem, but it's an error.
This kind of embarrassing taking in account how many hours I've spent on fixing this. The error was created because of an error in my routers.js
this.route('gameRoom', {
path: '/game/_:id'
})
Should be:
this.route('gameRoom', {
path: '/game/:_id'
})
Happy coding.
In short, I want to do:
Meteor.publish('items', function(){
return Item.find({categoryId: Categories.find({active: true} });
});
The flag 'active' as part of 'Categories' changes regularly.
I also tried unsub/resub to the Items collection by leveraging reactivity on the Categories collections, and it works, unfortunately it re-triggers on ANY modification to the Categories collection, regardless if it affected the 'active' flag or not.
What are my options?
Nothing solved the issue of the items not being 'deleted' locally when the category is flagged as inactive on the server. Solution (ish) is to:
Client:
Categories.find({active: true}).observeChanges({
added: function(){
itemsHandle && itemsHandle.stop();
itemsHandle = Meteor.subscribe("items");
}
});
Server:
Meteor.publish('items', function(){
var category = Categories.findOne({active: true});
return category && Items.find({categoryId: Categories.findOne({active: true}._id);
});
I realize this isn't perfect (still uses client side code), but it works and its the cleanest I could think of. I hope it helps someone!
A possible solution is to create a dependency object, watch for all categories change, and trigger the dep change if the active flag was toggled. Something along these lines:
var activeCount = Categories.find({active: true}).count();
var activeDep = new Deps.Dependency();
Deps.autorun(function() {
var activeCountNow = Categories.find({active: true}).count();
if(activeCountNow !== activeCount) {
activeCount = activeCountNow;
activeDep.changed();
}
});
Meteor.publish('items', function(){
activeDep.depend();
return Item.find({categoryId: Categories.find({active: true} });
});
Note: I'm only verifying whether the number of active categories have changes so that I don't have to keep the active list in the memory. This may or may not be appropriate depending on how your app works.
Edit: Two-sided flavor mentioned in the comments:
Client:
var activeCount = Categories.find({active: true}).count();
var activeDep = new Deps.Dependency();
Deps.autorun(function() {
var activeCountNow = Categories.find({active: true}).count();
if(activeCountNow !== activeCount) {
activeCount = activeCountNow;
activeDep.changed();
}
});
Deps.autorun(function(){
activeDep.depend();
Meteor.subscribe('items', new Date().getTime());
});
Server:
Meteor.publish('items', function(timestamp) {
var t = timestamp;
return Item.find({categoryId: Categories.find({active: true} });
});
Meteor.startup(function() {
Categories.find().observe({
addedAt: function(doc) {
trigger();
},
changedAt: function(doc, oldDoc) {
if(doc.active != oldDoc.active) {
trigger();
}
},
removedAt: function(oldDoc) {
trigger();
}
});
});
Now, the trigger function should cause the publish to rerun. This time it's easy when it's on the client (change subscription param). I'm not sure how to do this on the server - perhaps run publish again.
I use the following publish to solve a similar issue. I think it is only the one line nesting of queries that limits the reactivity. Breaking one query out inside the publish function seems to avoid the issue.
//on server
Meteor.publish( "articles", function(){
var self= this;
var subscriptions = [];
var observer = Feeds.find({ subscribers: self.userId }, {_id: 1}).observeChanges({
added: function (id){
subscriptions.push(id);
},
removed: function (id){
subscriptions.splice( subscriptions.indexOf(id)) , 1);
}
});
self.onStop( function() {
observer.stop();
});
var visibleFields = {_id: 1, title: 1, source: 1, date: 1, summary: 1, link: 1};
return Articles.find({ feed_id: {$in: subscriptions} }, { sort: {date: -1}, limit: articlePubLimit, fields: visibleFields } );
});
//on client anywhere
Meteor.subscribe( "articles" );
Here is another SO example which gets the search criteria from the client through subscribe if you decide that is acceptable.
Update: Since the OP struggled to get this going I made a gist and launched a working version on meteor.com. If you just need the publish function it is as above.