I’ve been using Meteor for years and I finally found a Blaze autorun
issue that has stumped me. The problem is waiting on multiple Collection updates in a single template before updating the UI.
Here’s an example illustrating the problem:
I’m working on a collaborative document editor. Think Google Docs. I have a Papers
collection with a page
field that holds the current page the group is on. At any time, any members of the group can update the paper to a the next/previous page
.
When collaboratively working on a paper, any user can enable several different “info views” on the paper. One view, for example, is a “Author Information” view that kicks out a side-column to the right of the paper that has a bunch of meta-data about the authors of the paper. This data is in a Authors
Collection and contains a showAuthors
field in the Authors
collection along with the data. There’s another view that can be enabled that shows the formulas that make up any embedded charts. That’s a collection called Formulas
and has a showFormulas
field along with the data.
Any time someone changes to a new page, the “Authors” and “Formulas” side-columns should automatically close and have to be reopened by someone on the new page of the paper.
So in the page viewing template there is an autorun
that looks like this:
instance.autorun(function() {
let currentData = Template.currentData();
// Listen for page to update
instance.currentPage.set(currentData.page);
// Listen for views to update
instance.showAuthors.set(currentData.authors.showAuthors);
instance.showFormulas.set(currentData.formulas.showFormulas);
});
So the idea is that we’re reactively listening for anyone to enable any of the side-column views and also update the page. The documents Authors
and Formulas
object are loaded onto the main Papers
document in the publication. SIDE NOTE: I’m using ReactiveVars
to control reactivity because the user can put the paper into a “local editing mode” and control the page and views without updating everyone else or receiving the server state, so I don’t connect directly to the actual data fields in helpers. Instead I use currentData()
in an autorun. I use intermediary ReactiveVars
to control the UI. This doesn’t/shouldn’t affect this issue.
Whenever a page is changed by any user from the server, the two views need to disable. So on the server for the Papers
collection I’m using matb33:meteor-collection-hooks and have a hook to hide the Authors
and Formulas
anytime a new page is updated:
Papers.before.update(function (userId, doc, fieldNames, modifier, options) {
if(modifier.$set && modifier.$set.page && modifier.$set.page != doc.page) {
Authors.update({paperId: doc.paperId}, {$set: {showAuthors: false}});
Formulas.update({paperId: doc.paperId}, {$set: {showFormulas: false}});
}
});
So what ends up happening is the above autorun
is ran three times. Once for the Authors
update, once for the Formulas
update, and once for the Papers
update.
The problem is in the UI this all happens in a glitchy linear order. First the “Authors” side-column hides, then the “Formulas” side-column hides, then the “Page” updates to the next page (I know that might sound cool - but in this case it looks really bad). What I’m trying to achieve is to somehow wait for all these updates to happen on the client then to a single update pass to the template. The above example is simplified and is actually more complex in my actual app. But this illustrates the issue.
I don’t need to use a loader because I actually load ten pages of the paper, the authors, and the formulas at a time. So there is no loading delay. All the data is already loaded. What I’m looking for is some way to not update the template until all three autoruns
have completed. Even if I separated the above autorun
into three separate autorun
blocks, you’d still have the same problem. Because the template is sourced from three different Collections, each time you update each of those Collections, the template updates one by one. Is there any way to handle this in Meteor? I tried playing with Tracker.afterFlush
but it always fires after each autorun
fires. There doesn’t seem to be a way to wait for multiple autoruns
to finish.
Trying to use some kind loader ReactiveVar
hack doesn’t work either. As the above autorun
has to also allow any users to show or hide any of the side-columns at anytime. So there’s no way to know if hiding a side-column is because a user directly wants to hide it or if it’s being hidden by the page
hook because a new page is being updated. I even thought about using a setTimeout
to wait on multiple autorun
updates but again… too hacky.
I guess in this use-case need to know why data is being updated - not just the the update of the data. Then I could shut off all the views in one pass of the autorun
but there’s no way of knowing why a view is being shut off.
I know one solution is to move the two fields over to the Papers
collection, then the update could come in one fell swoop to a single collection. But I’m curious if there’s a solution for this type of data design. As this could happen a lot in my app.