Fog of Code

Random thoughts about code, software and life

Archive for June 2013

Showing waiting indicator while Meteor renders

with 4 comments

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.

Written by xyand

June 30, 2013 at 9:55 pm

Posted in Meteor

Tagged with ,

Calling handlebars helpers explicitly from js

leave a comment »

I don’t suggest that it’s a good practice to call Handlebars helper to do work outside of the Handlebars templates context. However, sometimes it can come handy for quick tests/debugging. Let us consider two case.

The first when we want to use a global helper:

// Definition
Handlebars.registerHelper("myHelper", function() {...});

// Usage
Handlebars._default_helpers.myHelper.apply(context, argsArray);

And a template helper:

// Definition
Template.myTemplate.helpers({
    "myHelper": function() {...}
});

// Usage
Template.myTemplate._tmpl_data.helpers.myHelper.apply(context, argsArray)

Notice that we use apply to pass the helper’s context (this).

Written by xyand

June 10, 2013 at 2:28 pm