Archive for June 2013
Showing waiting indicator while Meteor renders
This was meant to be a short one but somehow I managed to write down all my attempts to tackle this issue. So if you just want to know how to do it, please skip to the last code example.
Imagine, hypothetically, that you have a large image gallery written in Meteor. Now imagine that this gallery has several views that the user can switch back and forth. It is very easy to implement using Meteor’s reactive data dependencies but it may have some side effects. Let us consider the following code:
Template.myGallery.events( "click #setView1": -> Session.set("viewCurrent", "view1") )
Changing the value of the “viewCurrent” session variable would probably trigger some reactive scope invalidations that would in turn trigger DOM manipulation. All is great, but it takes some time. In a single threaded js environment this would also cause the UI to hang and wait until the number crunching is over. So what can we do? A sensible solution would be to notify the users that the system is busy by showing some kind of waiting indicator. So lets try and trigger such indicator in Meteor.
Template.myGallery.events( "click #setView1": -> Session.set("isBusy", true) Session.set("viewCurrent", "view1") Session.set("isBusy", false) )
I assume that by setting the “isBusy” session variable, some template renders the waiting indicator into the DOM. Although this may look like a reasonable solution, in fact it is not. Lets consider what happens . When changing reactive variables, the computation re-rendering occurs when the client is idle. So in our case, only after leaving the event handler. We also know that when a flush occurs, all invalidated computations are re-computed recursively until there is nothing left to re-compute. In this case “isBusy” stays false
and “viewCurrent” changes to “view1”. As a result the waiting indicator is never displayed. So what now? Another attempt.
Template.myGallery.events( "click #setView1": -> Session.set("isBusy", true) Deps.flush() Session.set("viewCurrent", "view1") Session.set("isBusy", false) )
OK, now this gets better. Now we use Deps.flush()
in order to force Meteor to re-compute all invalidated computations, including rendering the waiting indicator. Actually, it almost does the trick. The only problem is that the UI doesn’t get a chance to be updated. As soon as we leave the event handler, Meteor begins re-computing all invalidated computations (including the library), never giving the UI a chance to update. Now lets give it a final try:
Template.myGallery.events( "click #setView1": -> Session.set("isBusy", true) # Force DOM manipulation Deps.flush() # Delay invalidation so the UI can be updated Meteor.setTimeout( -> Session.set("viewCurrent", "view1") Session.set("isBusy", false) 0 ) )
And that’s it! If anyone has a better way of doing it I’d really like to hear about it.