[SOLVED] How do I know when all collection elements have been rendered on the screen?


#1

Let’s say I have a simple case like:

<template name='items'>
  {{#each items}}
    {{> item}}
  {{/each}}
</template>

items collection changes and Meteor retrieves the new item set from the server ─ like in a item pagination case, for example.
How do I know whether all items have been rendered on the screeen?

items.onRendered doesn’t fire in this case and I cannot find a way to intercept the ready event from the DOM.

Do you have any suggesitons?


#2

Given that any user can add items in the collection at any time, are you sure “all items have been rendered on the screeen” is actually meaningful ? :- )


#3

I’m not sure what you’re trying to achieve - as @steve asks, is it meaningful?

what you might want to know is did the susbscription completion (and then assume that it was rendered if you’re using standard reactivity) in which case I would look at pub/sub .ready()


#4

I have many cases where I need this information. My last one is to handle infinite pagination. When I scroll my list upward it automatically loads another 50 records from the server. When the list is fully rendered I want to move the page to the last element of the previous set of records with an animation - exactly where the user was before loading another set of elements.


#5

What you actually want is to know when “all initial subscription elements have been rendered on the screen”. In that case, wait for the subscription to be ready, then use Meteor.setTimeout(0) to be sure the resulting data are rendered, then move the page to the last element.


#6

I guess that it is what @garrilla suggested in his reply. Do you mean something like this?

Template.messages.helpers({
	messages: function() {

        //do the usal stuff

       var that = this;
       var instance = Template.instance();
       instance.autorun(function() {
         if (instance.subscriptionsReady()) {
           //move the page
         }
       });
 ...
 });

Sorry, I don’t understand the Meteor.setTimeout(0) thing… could you please elaborate on that?

However, following this pattern how can I be sure that messages have been actually rendered on the screen?

I was think of using Tracker.afterFlush(functio() {...}) instead. Reading Meteor documentation I find out this comment in a code snippet:

Use Tracker.afterFlush to wait for the UI to re-render


#7

With Tracker.afterFlush seems to work:

....
Tracker.afterFlush(function(){
 if (Session.get('message-anchor-id') && $('#messages-div').scrollTop() === 0) {
     var pixelsToScroll = $('#' + Session.get('message-anchor-id')).offset().top;
     $('#messages-div').animate({scrollTop : pixelsToScroll}, 100);
 }
});

Thanks for your support!


#8

The typical use case is:

Template.myTemplate.onCreated(function() {  
  var self = this;

  // Subscribe
  self.autorun(function() {
    Meteor.subscribe('mySubscribe', whatever, Session.get('page'), count);
    ...
  });

  // When ready, animate
  self.autorun(function() {
    if (!self.subscriptionsReady())
      return;
    // Need defer or setTimeout(0) or afterFlush to wait until after rendering is done
    Meteor.defer(function() {
      self.$('.toAnimate').animate(...);
    });
  });
});

On client, defer() is equal to setTimeout(0). afterFlush() can also be used. I’ve never seen MDG commenting on which is better, all I know is they all work.

EDIT: yes, you found one comment from MDG about afterFlush() and rendering. So it is probably better to use afterFlush().


#9

Awesome, thanks. Good to know that pattern.
In my case I need to call Tracker.afterFlush in a helper function, so if I check for subscription readiness using a autorun block I got an error:

Can’t call View#autorun from a Tracker Computation; try calling it from the created or rendered callback

On the other hand, this is the only place where I can call afterFlush because onCreated and onRendered aren’t called anymore. In my understanding, however, afterFlush should guarantee that subscriptions are ready at the time its callback function is invoked my Meteor.


#10

Most of the time, you should not do anything in helpers, except returning data.

That is what the autorun is for: be called even if onCreated is not.