Order of startup, subscribe, onCreate on a page reload


#1

I’ve got this code:

    Meteor.subscribe('startup');

That I’ve since wrapped in this:

Meteor.startup(function () {
    Meteor.subscribe('startup');
});

To try and get it to run before this:

Template.newMessage.onCreated(function () {
   // some sync, non reactive code requiring collections from startup
});

when I’m refreshing on a page with the newMessage template.

But I realised while typing the question that it was a race condition and in fact, I’m guessing because onCreated, startup, subscribe etc… are all called within one js execution block so the async returns will always happen after these functions are called (so it’s not a race condition).

My options are therefore to make use of IR’s waitOn or make my onCreated code reactive. I choose the latter.

I need validation and confirmation.


#2

you subscribed, then you have to make sure subscription is ready before use.
Because you need to get data in onCreated, this region not reactive.
You can get data in helpers or check data in Tracker


#3

If you try to think around this, you’ll eventually hit another problem.

Instead, you should think in async terms and utilize the tools that meteor provides.

One attack at the problem would be to register a new reactive var with an initial falsy value, set that reactive var truthy on the subscribe callback. And then in your onCreated block, set up a Tracker that runs your code and depend on the reactive var being truthy.

Depending on the nature of what you are actually doing with that “some sync non reactive code” there may even be much better solutions.


#4

oh boy, now you’ve really asked for it…

Template.newMessage.onCreated(function () {
    var self = this;
    self.dict = new ReactiveDict('newMessage');
    // use activeOrganisation as a default
    // if it hasn't been subscribed to yet (edge case when the first page is newMessage)
    // then track it until it's loaded
    if (!Mk.Subs.startup.ready()) { //<-- *UGLY* workaround for when the sub isn't ready
        Tracker.autorun(function () { 
            self.dict.set('org', Meteor.user().activeOrganisation);
            this.stop();
        });
    } else {
        self.dict.set('org', Meteor.user().activeOrganisation);
    }

    Tracker.autorun(function () { //<--- tracking the initial falsy value 
        Meteor.subscribe('organisation', self.dict.get('org'));
    });
});

I think this embodies what you’re talking about but that horror in the middle was my way to get around a ‘default’ value the template could be setup with.


#5

Ok, this whole code looks to me like it can be written as:

Template.newMessage.onCreated(function(){
  var instance = this;
  instance.autorun(function(){
    instance.subscribe('organisation', Meteor.user().activeOrganisation);
  });
});

And then you can use Template.instance().subscriptionsReady() helper in your template to decide what to show. Either the user’s organisation data, or something else.

Also I don’t quite see the startup dependency in that code. To be frank, the code from your second comment does not piece together with the code from your first comment.

If the template level subscription works for you, I’m glad it does. But if it does not, I’d be happy to dig in deeper.


#6

Re startup dependency, that was me not understanding subscriptions are async. Please ignore that.

Didn’t know about subscriptionsReady, that’s really cool. I didn’t want to use IR’s waitOn as I’m thinking of moving later.

Re ReactiveDict, I didn’t mention that the organisation’s scope is template based. I don’t want to have a dropdown selection saved directly to the user collection. activeOrganisation is a global collection level variable, and self.dict['org'] is a template level variable which uses activeOrganisation as a default value when the subscription is ready.


#7

You could use Session for global variables.

Also check these out:

Also, you should take an hour off to read up on flow router. It is extremely easy to migrate to and will make your life easier.