To set the context, this is the use of Template subscriptions together with Template autorun in the onRendered function.
The official documentation suggests this form:
Template.something.onRendered(function() {
var self = this;
self.autorun(function() {
self.subscribe("name", function() {
var thing = Collection.findOne(); // some reactive operation
// do fun stuff with thing
});
});
Which unsubscribes/resubscribes to the name publication reactively.
And that works very nicely. However I have used this form:
Template.something.onRendered(function() {
var self = this;
self.subscribe("name", function() {
self.autorun(function() {
var thing = Collection.findOne(); // some reactive operation
// do fun stuff with thing
});
});
Which also works very nicely - and feels more “right” to me. Subscribe only once and let reactivity do its thing.
I think you would use the first snippet over the second when you have reactive arguments that you pass into the subscription call. Otherwise I think it makes no difference.
That does make sense, but when you say “no difference”, surely there is more overhead in unsubscribing and resubscribing - or is that ignored because the subscription’s not actually changed?
I would say that you should prefer the 2nd form if you don’t have reactive arguments to the subscription, whether or not Meteor figures out on its own if it actually needs to un-/resubscribe instead of just staying suscribed. Whether it does already figure that out I don’t know.
The reason for preferring the 2nd form is (obviously?) that stuff that doesn’t need to rerun shouldn’t have to, i.e. proper scoping / structure of code that aligns with what behavior is actually desired. EDIT: I suppose that’s why the 2nd form feels better to you as well.
If you call Meteor.subscribe within a reactive computation, for example using Tracker.autorun, the subscription will automatically be cancelled when the computation is invalidated or stopped; it’s not necessary to call stop on subscriptions made from inside autorun. However, if the next iteration of your run function subscribes to the same record set (same name and parameters), Meteor is smart enough to skip a wasteful unsubscribe/resubscribe.
I’m currently using Iron Router for subscriptions, but I’m slowly migrating to Template subscriptions. This is how I’m doing it so far:
Template.template_name.onCreated(function() {
var instance = this;
instance.state = new ReactiveDict;
instance.autorun(function() {
var subscription1 = instance.subscribe("Collection_Name1");
var subscription2 = instance.subscribe("Collection_Name2", Session.get('session_var'));
if (subscription1.ready()) {
var record = instance.document();
// Reactive vars
if (record) {
instance.state.set('reactive_var', record.field);
} else {
instance.state.set('reactive_var', null);
}
}
});
instance.count = function() {
return Collection_Name1.find({}).count();
}
instance.document = function() {
return Collection_Name1.findOne({
_personId: Meteor.userId()
});
}
});
Template.template_name.helpers({
setFormMode: function() {
var count = Template.instance().count();
var data_set = Template.instance().document();
if (count === 0) return {
doc: null,
type: "insert"
};
else if (data_set) return {
doc: data_set,
type: "update"
};
}
});
Will someone please review this for correctness? What would you do differently?
For example,
What’s so wrong with putting the subscription1 inside of autorun; will I still get updates to subscription1 data if this is outside autorun? How would you do it?
Why but the subscriptions in onRendered vs onCreated and vis-versa?
I use instance.document to access query the published cursor, and is used by the helper methods. What would you do?
I haven’t tested this, but instead of if (subscription1.ready())), try if (instance.subscriptionsReady()). This will cover all the subscriptions your template is waiting for.
Putting subscription1 inside an autorun is useless (it will even cause a (very minor) overhead). A subscription doesn’t need an autorun to update the data.
subscriptions2, on the other side, needs an autorun, because one of its arguments might change reactively.
You usually put subscriptions in onCreated, so that they are run as soon as possible and while the initial DOM is built. There is usually no need to wait for onRendered.
I wouldn’t do that. I think your collection access functions belongs with the collection, not with the template:
.API is a bit awkward, but I do like the general idea of this. I say “awkward” because we generally don’t put “what something is” into “the name we call it”, e.g. I say “my macbook”, not “my macbook laptop personal computer”, even though my MacBook is indeed a laptop / personal computer. 50% of programming (or maybe 33%) is finding really good names for things, or at least avoiding the absolutely terrible ones, and calling an API “something something API” is not mega terrible, but easy enough to improve – and very easy to spot, too.
I understand that you’ve probably just been using ANY name for that, but I’m just stating this because such things actually happen in real code, and it’s good to pay attention to it. AND, as I said, I really like this idea of extending the standard collection API with our own, “user-defined” methods, and I’m trying to think of a proper namespace to put them in for myself.
Maybe .ext could work, like Meteor.users.ext.findAdminUsers(), Products.ext.findNewAdditions(), Threads.ext.archiveOlderThan(30, 'days')…
Cool, thanks for linking that. Currently really getting into the depths of Meteor development and good to see thoughts on structure, organization etc from others who’re also spending lots of time working in and thinking about Meteor! (Are you part of percolate? I don’t really know most of the people here on the forums, begun participating only a week or so back!)
EDIT: I suppose this message was a bit off-topic. Feel free to respond in a PM if you like, that’s probably more appropriate
If I don’t put subscription1 inside a autorun, the ‘reactive_var’ reactive variable will not get set because the ready() will not get called on this publication. Or another way, If I want ready() to get called and set the reactive var, I need it inside a autorun from what I found.
A reason why you might want your subscription inside the autorun is if it’s subscribing based on reactive parameters. A common example would be paginating a publication with a limit parameter.
In that case, you do want the subscription to re-run every time that parameter changes, so I’m pretty sure you would put it inside the autorun.
+1 but @aadams do not use log in/out - so publish seems not to re-run. Or you can confirm that Meteor.connection.setUserId(userId) works like log in/out?