When is DOM really ready/rendered


#1

When I subscribe to a collection and then iterate over the cursor, when do I know all the items in the cursor have rendered?

In the below example, the Template.messages.onReady fires before the list items have been added.

  Template.messages.helpers({
    messages: function() {
      return Messages.find();
    },
  });

<template name="messages">
  <ol class="messages">
  {{#each messages}}
    <li>{{subject}} <a class="delete" id="{{_id}}" href="#" title="delete">x</a></li>
  {{/each}}
  </ol>
</template>

I think Meteor needs an #each.onReady or something like that so that actions can be performed when the DOM truly is ready.

Or am I missing something?


#2

Good question.
When would it be ‘truly ready’, what if there is a reactive change to your Messages collection - does that un-ready and re-ready when complete?


#3

This is one thing I don’t like about Blaze. It’s unpredictable as far as when things are truly rendered. There’s onRendered, but sometimes things aren’t really done yet, maybe because of some template logic. In that case, it’s usually Tracker.afterFlush to the rescue. But then factor in loading images, and onRendered won’t help you there. I coded my own event to fire off as soon as all images in a template are loaded, and that really helped.

I feel like there needs to be a Template.xxx.onLoaded method.


#4

I think the problem is that there isn’t any way to determine when ready would be fired. Reactive data could be added or removed at any time.

Depending on the situation, this can be a difficult issue to deal with… The best way to deal with this is to create a template at the level where the things change and handle things there.

So in this case:

{{#each messages}}
    {{>message}}
{{/each}}

<template name="message">
    <li>{{subject}} <a class="delete" id="{{_id}}" href="#" title="delete">x</a></li>
</template>
Template.message.onRendered(function(){
    //handle the problem here
}):

#5

How would you handle the problem in the onRendered of the individual message? How do you know that it is the first, nth or last message template that is being rendered?


#6

You really don’t, can you give me an example of what you need to accomplish?


#7

Lets say I want to stagger animate the results of my subscribe/find, I first would need to add the results to the DOM, and when the last element has been added, I need to fire Materialize.showStaggeredList('#staggered')


#8

I have been working with Blaze for a while now, and this is one of the most vexing problems, especially confusing new comers to Meteor.


#9

Yeah I can see how it can be an issue, and confusing to those that are new to Meteor. To be fair though this isn’t really an issue with blaze and more an issue with live data. With ajax it’s very simple to reason about and know when things are going to be in a certain state because you make a request for data, then use that data to write to the DOM. A reactive rendering engine coupled with with livequery though isn’t that simple.

In the case of showing a staggered list you can do something like checking if the item rendering is the the last one in the collection… If so then show the staggered list.

Template.message.onRendered(function(){
    var lastMessage = MessageCollection.findOne({}, {sort:{createdAt:-1}});
    if(this._id == lastMessage._id){
        Materialize.showStaggeredList('#staggered');
    }
});

#10

how about using the ready() in the subscription handle then autorun the Materialize part ?


#11

Because a ready subscription does not mean data has been added to the DOM.


#12

How about using afterFlush in helper?


#13

I have similar problems I would really like a {{#each }}.changed function that informs me everytime the reactive data source changes.
That way I can respond to the Dom changes.

I think since this is a common problem there should be something better than helper and tracker.afterflush workarounds.


#14

Observe the reactive data source directly from an autorun or observeChanges (if it is a cursor), then use tracker.afterflush(). This has worked well for me.


#15

I think the Tracker.afterFlush pattern makes sense tbh, and I use it quite a bit. If you define an autorun to observe a cursor or subscriptionsReady, then it makes sense that the DOM will only be available with the new data after the next template flush.