Concatenate new Iron Router subscriptions to existing ones - meteor

Let's say I have a RouteController with a set of subscriptions in waitOn. If I create a route based on that controller, is there a good way to say "wait on all of the subscriptions from the controller, but in addition subscribe to this".
In other words I want to concatenate to existing waitOn subscriptions rather than completely override them. Is there a good way to do this?

I ended up using this approach of using child and parent controllers. You can access the parent call with __super__. Here's an example:
ParentController = RouteController.extend({
waitOn: function() {
return [
Meteor.subscribe("foo"),
Meteor.subscribe("bar")
]
}
})
ChildController = ParentController.extend({
waitOn: function() {
return _.extend(
ChildController.__super__.waitOn.call(this), [
Meteor.subscribe("baz"),
Meteor.subscribe("fiz")
]
)
}
})

Related

Meteor.user with Additional Fields on Client

In Meteor, one can add additional fields to the root-level of the new user document like so:
// See: https://guide.meteor.com/accounts.html#adding-fields-on-registration
Accounts.onCreateUser((options, user) =>
// Add custom field to user document...
user.customField = "custom data";
return user;
});
On the client, one can retrieve some data about the current user like so:
// { _id: "...", emails: [...] }
Meteor.user()
By default, the customField does not exist on the returned user. How can one retrieve that additional field via the Meteor.user() call such that we get { _id: "...", emails: [...], customField: "..." }? At present, the documentation on publishing custom data appears to suggest publishing an additional collection. This is undesired for reasons of overhead in code and traffic. Can one override the default fields for Meteor.user() calls to provide additional fields?
You have a couple of solutions that you can use to solve this.
Null Publication
Meteor.publish(null, function () {
if (this.userId !== null) {
return Meteor.users.find({ _id: this.userId }, { fields: { customField: 1 } });
} else {
return this.ready();
}
}, { is_auto: true });
This will give you the desired result but will also result in an additional database lookup.. While this is don't by _id and is extremely efficient, I still find this to be an unnecessary overhead.
2.Updating the fields the Meteor publishes for the user by default.
Accounts._defaultPublishFields.projection = { customField: 1, ...Accounts._defaultPublishFields.projection };
This has to be ran outside of any Meteor.startup blocks. If ran within one, this will not work. This method will not result in extra calls to your database and is my preferred method of accomplishing this.
You are actually misunderstanding the documentation. It is not suggesting to populate and publish a separate collection, just a separate publication. That's different. You can have multiple publications/subscriptions that all feed the same collection. So all you need to do is:
Server:
Meteor.publish('my-custom-user-data', function() {
return Meteor.users.find(this.userId, {fields: {customField: 1}});
});
Client:
Meteor.subscribe('my-custom-user-data');

Meteor subscription doesn't refresh despite WaitOn

I'm using Iron Router. I have a RouterController that looks something like this:
var loggedInUserController = RouteController.extend({
layoutTemplate: "GenericLayout",
waitOn: function () {
return Meteor.subscribe("TheDataINeed");
}
});
And I have a route defined which uses this controller to wait for the 'TheDataINeed':
Router.route("/myapp", {
name: "Landing",
controller: loggedInUserController,
data: function () {
if(this.ready()){
return {content: "page-landing"};
}
}
});
Now, the problem is the data I am subscribed to is conditional: meaning, depending on the user's role, I publish different data, like so:
if (!Roles.userIsInRole(this.userId, 'subscribed') ) {
return [
myData.getElements({}, { fields: { _id: 1, title: 1}, limit: 5 })
];
} else {
return [
myData.getElements({}, { fields: { _id: 1, title: 1} })
];
}
When the user's role is not 'subscribed', I limit the published data to 5 elements.
The problem is publishing is not reactive, so when the user changes his role for the first time to 'subscribed' and I navigate to my route ("/myapp"), the user still sees the limited number of elements instead of all of them.
Is there a way to manually re-trigger the subscription when I am loading this route? If possible, I'd like to do this without adding new packages to my app.
Not sure about that approach but can you try to set session value in route instead of subscription code. Then in a file on client side where your subscriptions are you can wrap Meteor.subscribe("TheDataINeed") in Tracker.autorun and have a session as a subscription parameter. Every time that session value is changed autorun will rerun subscription and it will return you data based on a new value.

Meteor: Publishing all user not working without autopublish package

I would to show a list of all users, in my template.
I have:
//publications.js
Meteor.publish('users', function() {
return Meteor.users.find({}, { fields: {username: 1, profile: 1} });
});
//router.js
Router.route('/users/add/:_id?', {name: 'users.add', controller: 'UserAddController'});
UserAddController = RouteController.extend({
subscriptions: function(){
return [ Meteor.subscribe('hospitals'),
Meteor.subscribe('roles'),
Meteor.subscribe('users') ];
},
action: function() {
this.render('addUser', {
data: function(){
return { hospital_id : this.params._id }
}
});
}
});
//client
Template.listUsers.helpers({
users: function() {
return Meteor.users.find({});
}
});
But the list keep showing only the current logged-in user. I have created a list of users using Account.createUser() function What am I doing wrong?
Thanks.
You have to subscribe to a publication using this.subscribe() in subscriptions hook:
// a place to put your subscriptions
subscriptions: function() {
this.subscribe('items');
// add the subscription to the waitlist
this.subscribe('item', this.params._id).wait();
}
Or use waitOn:
// Subscriptions or other things we want to "wait" on. This also
// automatically uses the loading hook. That's the only difference between
// this option and the subscriptions option above.
waitOn: function () {
return Meteor.subscribe('post', this.params._id);
}
By default, Meteor publishes the current user. I see that you have a addUser template and a listUsers template. The problem is that while addUser is subscribed to the users publication, listUsers is not (this would depend on what else you have in your router of course). To fix this, change the call to this.render to render the listUsers template. Then, your users helper should work, and you can render the information however you like.
I tested this with my own app (the Microscope project from DiscoverMeteor) and it worked for me. Hope it works for you too. Comment here if not, and be sure to accept this answer if it worked. =)

How do I unsubscribe from the subscription set by Iron Router's waitOn?

I have a list of items fetched from a collection and I want to make it show different stuff depending what button is clicked. I figured I'd do this via sessions that update the publication.
Here's my publication:
Meteor.publish('stuff', function(filter) {
var nameVar = filter || "default"
return myStuff.find({name: nameVar})
})
Which I subscribe to in my router:
Router.route('/', {
name: 'home',
layoutTemplate: 'homeLayout',
waitOn: function() {
return Meteor.subscribe('stuff', Session.get('filter'));
}
});
Then make the buttons do their thing (they each have an id that matches what I want to filter by):
Template.buttonFilter.events({
'click button': function(event, template) {
var buttonId = event.currentTarget.id
Meteor.subscribe('stuff', Session.set('filter', buttonId))
}
})
However, when I click a button it won't unsubscribe from what I first subscribed to via the router, only from what was set on my last button click. I don't really understand why or how to fix it.
Edit: I don't believe this to be a duplicate since the answers to the other questions talk about changing routes, which I'm not doing.
It sounds like what you want (correct me if I'm wrong) is not so much to unsubscribe from the prior search, but to reactively subscribe to the current search and drop all records that are not encompassed by it. If so, you don't need to manually manage unsubscribing - Meteor will do that for you; you just need to make your subscription reactive.
Iron Router's waitOn will not rerun each time the subscription changes. To do that there are a few options. The quick and dirty option is to move the subscription from waitOn to onBeforeAction which will be rerun each time:
Router.route('/', {
name: 'home',
layoutTemplate: 'homeLayout',
onBeforeAction: function() {
Meteor.subscribe('stuff', Session.get('filter'));
this.next(); //make sure to include
}
});
Alternatively you can do it outside the router:
Tracker.autorun(function(){
var filter = Session.get('filter');
Meteor.subscribe('stuff', filter);
});
In either case you don't need to change the subscription from template.events - just change the session value.
Template.buttonFilter.events({
'click button': function(event, template) {
var buttonId = event.currentTarget.id;
Session.set('filter', buttonId);
}
});

Meteor Iron Router Run function when collection changes

Im new to Meteor and Im trying to figure out how to run a function after a collection change.
I have a route(iron router) that subscribes to a collection with waitOn. Which just waits for the subscrition to be ready before rendering which is what I want.
waitOn: function () {
return Meteor.subscribe('collection', this.params._id);
},
Any changes to the collection will be updated on all the clients and rendered automatically.
How would I run a function once the collection has changed?
You can use the onData hook, provided that you're returning that data using the data helper. E.g this is what a route may look like
this.route('routename',
path : '/abc',
waitOn: function () {
return Meteor.subscribe('collection', this.params._id);
},
data: function() {
return Collection.findOne({_id: this.params.id});
}
onData: function() {
//Do something when the data found by the above changes
}
});

Resources