How should I make sure that a reactive DOM element has been loaded?

226 views Asked by At

I have a template image where I display images. The image display toggles between canvas and img tags, depending on if the user just created the image or if it is already on server.

I toogle between img and canvas using a reactive dictionary called pageSession where I store a boolean reactive variable called displayMode. I have an helper currentDisplayMode used to return displayMode value.

So the helper looks like this:

Template.image.helpers({
    "currentDisplayMode" : function(){
        return pageSession.get ("displayMode");
    }
});

and in my template, I load the canvas or the img depending on currentDisplayMode like this:

{{#if currentDisplayMode}}
  <img src="{{source}}"width="215" height="215"/>
{{else}}
  <canvas id="canvas" width="215" height="215"></canvas>
{{/if}}

1. the problem.

If I try something like this:

pageSession.set("displayMode", false)
var canvas = template.find('#canvas')
//do stuff related to canvas

I can't find my canvas because the UI isn't updated yet.

2. My dirty fix

I use a Meteor.setTimeout() like this to fix it:

    pageSession.set("displayMode", false)
    Meteor.setTimeout(function(){
        var canvas = template.find('#canvas')
        //do stuff related to canvas
        }, 100);

3. My question

This fix works so far but I'm worried about cases where 100ms wouldn't be enough to update the UI. Moreover, I don't feel like it is a reliable solution.

Note that it is not just a binary toggle (image or canvas). The canvas need to be re-rendered depending on 3 different modes (identicon, photo, imageFile). Each mode can be re-rendered as well.

To sum it up, I need to fill properly my canvas in several cases (once I make sure it has been rendered before):

  • when displayMode toogles: from <img> to <canvas>
  • when I fill my canvas with another method: from identicon to photo for instance
  • when I fill my canvas with the same method, but different data: e.g. I reload another identicon or I load another photo. How would you proceed?

I tried two other approaches, both by moving the canvas into a dedicated template:

  • Blaze.render() the template in a target div and attach to the canvas template rendered function my code. It makes it difficult to control what is or what isn't loaded: I need to remove previously rendered canvas templates.
  • the workaround explained here: Meteor: Forcing rerendering whole template after collection update with Blaze did not work either (I haven't tried hard, it does not look like a clean workaround).
1

There are 1 answers

0
Billybobbonnet On BEST ANSWER

I finally managed to solve this. Basically, what I wanted was to be sure that my canvas was rendered before doing one of my 3 filling tasks (identicon, photo, imageFile).

The first step is the easy one: I use a page Session reactive variable flag canvasLoaded that I update on my rendered and destroyed canvas template functions.

The second step is something I already used to make sure a file was uploaded (and its url available) on form submit. This is a waitfor function I found here : https://stackoverflow.com/a/14811679/3793161

So what I do is to wait for my flag canvasLoaded to switch to true in the waitfor function. In the callback of waitfor I just call a loadCanvas function where I load the canvas according to the current filling mode and parameters (all stored in reactive dictionary variables).

Et voilĂ ! :-)