I recently moved away from autopublish and have been trying to update my application to gracefully deal with the latency introduced by this change. I have been using a combination of template and router level subscriptions. The template level subscriptions work beautifully but I have been having issues with the router level subscriptions.
I am doing router level subscriptions in the subscriptions
function of the route and I often populate the data
function with documents from these subscriptions. I don’t want to render the page before the subscriptions are ready, because then I would have to check that the subscriptions are ready in whenever I access a document from those subscriptions.
The standard solution seems to be to either use a waitOn
or to manually check this.ready
before rendering anything in the router.
action: function() {
if (!this.ready())
this.render('loading');
else
this.render();
}
However, this creates undesirable page flickering if it only takes a second to load the subscriptions (i.e. the user will click a link, the loading screen will show for ~.1 seconds, and then the new page is loaded).
Another solution, which I personally prefer, is to not leave the current page until the subscriptions for the next page are ready.
action: function() {
if (!this.ready())
return;
else
this.render();
}
But this has a whole other set of issues. For example, if the route for the next page is missing a subscription that the current page needs, then the current page will re-render and will of course throw an exception because it is missing data. For example, suppose the current route looks like:
Router.route('/projects/:projectID', {
name: 'projectOverview',
action: function() {
if (!this.ready())
return;
else
this.render();
}
subscriptions: function() {
return [Meteor.subscribe('project', this.params.projectID)];
},
data: function() {
return {project: Projects.findOne(this.params.projectID)};
}
);
<template name="projectOverview">
{{project.name}}
<a href="{{pathFor route="tasksOverview"}}">Tasks Overview</a>
</template>
and that the new route (after you click the Tasks Overview link) is:
Router.route('/tasks', {
name: 'tasksOverview',
action: function() {
if (!this.ready())
return;
else
this.render();
}
subscriptions: function() {
return [Meteor.subscribe('tasks')];
},
data: function() {
return {tasks: Tasks.find()};
}
);
<template name="tasksOverview">
You have {{tasks.count()}} tasks
</template>
Then after you click the link in the first route, you would get an error that project
is undefined when you try to access project.name
. This error happens because as soon as you click the link, the first route is torn down and the new one is created, so the subscriptions maintained by the first route are also torn down. And so project
is no longer available.
Ideally, I would like for there to be a way for the old subscriptions and data context to not get torn down until the new ones are ready. But I cannot think of a good way to make that happen.
I feel like this is a problem that basically everyone that uses Meteor has to deal with, so I am curious what solutions people have come up with.