Template.instance().subscriptionsReady() usage

I’m not clear how Template.instance().subscriptionsReady() works.
Why doesn’t Meteor wait for subscriptions get ready?

Template.channel.onCreated( function() {
	this.subscribe('channels');
	if (Template.instance().subscriptionsReady()) {
          //initialize data structures
     }
});

In my understanding it should wait for channels subscription (in this case) is ready and then let me initialize data structures.

2 Likes

PART 2
I observed that calling this.subscribe and then this.subscriptionsReady() inside onCreated callback gets the subscription not ready 100% of the times (in my app). Moreover, calling subscriptionReady it’s like just asking for the status of the subscription on the client side and doesn’t force the client to wait for the subscription data gets downloaded.
On the other hand if I use Template.subscriptionsReady in template HTML file, then the client waits for publications data before executing helpers functions. I don’ t understand why the subscriptionsReady behavior changes so much from the js file to the html file – in my opinion it should be consistent between the 2 of them.
Maybe I missed something here, so feel free to show me the way :smile:

However, because I actually want to get the client waiting for the subscriptions data needed before showing anything on the screen, I used the following workaround:

  1. I created a template helper function init() and I put inside the initialization logic for my template
  2. Then I called this init() in the html template, like this:
    <template name="layout"> {{#if Template.subscriptionsReady}} {{init}} ...
    It seems to be ok, but my feeling is that there should be a better way to get want I want.
    Any suggestions are welcome.
  • Calling this.subscribe() and then this.subscriptionsReady() inside onCreated callback should wait for the subscription to be ready 100% of the times. Now maybe you are confused about what a “ready subscription” actually is. Please describe more of your problem.
  • I think Template.subscriptionsReady just calls instance().subscriptionsReady() (see code here), so there is no reason why they would behave differently.

In my understanding a subscription is ready when it items values are aligned between client and server. So when I test a subscription readiness I expect that it works like a sync point between client and server, so I can be sure that any changes made by whatever client or server task has been aligned with with my end point.

Yes, but for some reason it doesn’t work in my case. I’m sure I made something wrong but for now the workaround I found seems to be pretty effective.

Not, none of these wait for anything, they just return true or false.

But .ready() is reactive, so if it changes and is used inside autorun, it reruns that function

same way as it reruns evaluation of template when you use it in html.

2 Likes

for more info see for example this https://www.discovermeteor.com/blog/template-level-subscriptions/

This is not always true. Consider this very common use case:

Template.channel.onCreated( function() {
  var self = this;
  self.autorun(function() {
    self.subscribe('channels', Sessions.get('channelColor'));
    	if (Template.instance().subscriptionsReady()) {
      //initialize data structures
    }
  }
});

Suppose that there are 2 blue channels and 3 red channels (no overlap). Initially, channelColor is blue and the 2 blue channels are loaded.

Now suppose channelColor changes from blue to red. Because of the autorun, this will result internally in 2 function calls (pseudocode):

  1. self.subscribe('channels', 'red')
  2. blueSubscription.stop()

The subscription will be marked as ready just after step 1, at a time when the client has the 5 channels in its local cache ( 2 blue + 3 red). Consequently, if you access the collection inside the if (Template.instance().subscriptionsReady()) block, you will see 5 channels, not 3.

Then, after a few milliseconds, the “blue” subscription will be stopped and the 2 blue channels will be removed from the client.

This problematic behavior is described here.

2 Likes

I knew pretty well this article, but I’m not clear how to handle readiness of a subscription as a sync point in my program.
Do you mean I need to check readiness of a subscription within autorun block?

This is my usual case pattern:

Template.layout.onCreated(function() {
    this.subscribe('channels');
    if (this.subscriptionsReady()) 
        Session.set('current-channel', Channels.findOne());
}

In order to store a value in current-channel session variable I need to be sure that channels subscription has been aligned on the client. On the other hand, current-channel needs to be valued before my channels template renders on the screen, because I’m going to use that session variable to set the default channel the first time the application loads.
My workaround was to create a init() helper and put it at the beginning of the outer html template within a {{#if Template.subscriptionsReady }} block.

How do you handle this using autorun instead?

Excellent sample. I’ll try it soon in my code.

I have 2 questions for you:

  1. It’s probably a stupid question, but the data structures I need to initialize are used in helpers functions of the same template. What happens to that part of my code? Does the page rendering wait to execute until subscriptions get ready? I suppose it should work like that.
  2. In some circumstances I have a similar need in Bootstrap modal dialogs. In that case I need to manage data structures initialization on shown.bs.modal event. Can I do exactly the same thing and then use Template.autorun callback?

As a final consideration I have to say that this reactive mechanism to synchronize data between client and server is really hard to understand and use. In a framework like Meteor that should be definitely an easier thing to do.
Why not having something like Template.subscriptions.wait(function(){..}) directly in the framework?

1 Like