1

Hi fellow Meteor friends!

Please note: I am using Tom's router!

So I'm trying to only display my template when the mongo collection is ready but for some reason it does not work! :(

I first followed this post: LINK

So I have my publish functions in the server.js and I subscribe to these functions inside my router, so no Deps.autorun() involved here (btw: is this the right approach? Deps.autorun() did not work for me properly):

So I have something like:

'/myroute': function(bar) {
Meteor.subscribe("myCollection", bar, function() {
        Session.set('stuffLoaded', true);
    });
    return 'stuffPage';
}

In the template, where the data loaded from "myCollection" is displayed, I will have something like this:

<template name="stuffPage">
    {{#if stuffLoaded}}
        <!-- Show the stuff from the collection -->
    {{else}}
        <p>loading!</p>
    {{/if}}
</template>

For some reason "loading!" is never displayed. Also, for a couple of milliseconds, the "old data" from the last time the same template was displayed (but with another "bar" value provided to the publish function --> different data) is displayed.

This of course is not good at all because for a couple of ms the user can see the old data and suddenly the new data appears. To avoid this "flash" I want to display "loading!" until the new data is loaded but again: this does not work for me! :-(

What am I doing wrong?

Thx in advance for your help!

EDIT:

Ok so the problem with the answer in the first post provided by @user728291 is the following:

For some reason the router stuff get's called AFTER the Deps.autorun() ... what is wrong here? :( (please note: eventsLoaded == stuffLoaded.) Where do you guys put your Deps.autorun() for the subscriptions or in other words: What's your code mockup for this?

my console.log

I actually really think that my code mockup is just plain wrong. So how do you make different subscriptions based on the route (or in other words: based on the template which is currently shown)?

AND: Where do you put the Deps.autorun()? Inside the router.add() function? Or just inside of (Meteor.isClient)?

Community
  • 1
  • 1
Patrick DaVader
  • 2,133
  • 4
  • 24
  • 35

3 Answers3

2

Better to put the subscription in a Deps.autorun and use Session variable to pass arguments from the router. Also, make sure you are setting stuffLoaded to false before the subscribe runs. Otherwise it just keeps its old value.

'/myroute': function(bar) {
  if ( ! Session.equals( "bar", bar ) ) {
      Session.set( "stuffLoaded", false);  //subscription needs to be run
      Session.set( "bar", bar );  // this change will trigger Dep.autorun     
  }
  return 'stuffPage';
}

Deps.autorun ( function (){
  Meteor.subscribe("myCollection", Session.get( "bar" ), function() {
    Session.set("stuffLoaded", true);
  });
});

You might need some initial default values for the Session variables if you are not getting what you want on the first time the page loads.

user728291
  • 4,138
  • 1
  • 23
  • 26
  • please see my comment on the post below yours! – Patrick DaVader Aug 07 '13 at 00:37
  • I also tried your exact approach now and the "stuffLoaded" Session var always stays false for some reason. I understand your thinking behind it and agree that this should work but for some reason it doesn't! EDIT: OK so Session.set("stuffLoaded", false) inside the router is the problem. for some reason, because of this, "stuffLoaded" is always false, although it is set to true inside the subscribe for some time. – Patrick DaVader Aug 07 '13 at 00:45
  • one last thing: I put console.log's everywhere and the reason is: the Session.set("stuffLoaded", false) is fired AFTER the collection was loaded and therefore stuffLoaded was true. and before the (async) template can react to show the data, stuffLoaded already is equal false again because of the router. – Patrick DaVader Aug 07 '13 at 00:53
  • I am not sure how the Session.sets could be running in the wrong order but that would certainly be a problem. I also wonder if the problem might be single vs double quotes. I will amend answer above. – user728291 Aug 07 '13 at 00:54
  • please also see my edit in the first post. Just put a screenshot of my console.logs there. – Patrick DaVader Aug 07 '13 at 01:00
  • According to console messages it ends with `stuffLoaded` getting set to `false`, `bar` not being updated (?), and Deps subscribe not getting rerun? I think you should update your question with the code you are running. – user728291 Aug 07 '13 at 01:18
  • I tried it without the bar, So I would just make a standard subscription without passing any bar variable to it. So no bar involved here. The problem is that stuffLoaded might be set to true after the collection is loaded but AFTERWARDS (before the Template, which again is async, can react) the router is fired and sets stuffLoaded back to false --> no data displayed. – Patrick DaVader Aug 07 '13 at 01:29
  • It is the change in the Session variable "bar" that would trigger Deps.autorun to update the subscription. Otherwise subscription runs once, sets `stuffLoaded` to `true`, and after that `stuffLoaded` will only be updated by being set to `false` by the router. – user728291 Aug 07 '13 at 01:36
  • So what should I put into the Session var "bar" then? the currentPage for example? In the case I've tried Deps.autorun() should only be fire once since a whole collection is loaded. So that is fine in this case. Problem is that the collection is loaded but the data is not displayed at all. Again: If I understand your code correctly the Session var "bar" is something that changes the data I get from my publish function. That makes sense and everything but again it should also work for one of my cases where this is not needed and just one whole collection is loaded. – Patrick DaVader Aug 07 '13 at 03:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34958/discussion-between-user728291-and-patrick-schubert) – user728291 Aug 07 '13 at 05:01
2

I think @user728291's answer is pretty spot on, I'd just add that Meteor.subscribe returns a handle that you can use to check readiness:

Keep a reference to the handle

Deps.autorun(function() {
  stuffHandle = Meteor.subscribe(Session.get('bar'));
});

Then check it in your template:

{{#if stuffHandle.ready}}
  ...
{{/if}}

Template.barTemplate.helpers({stuffHandle: stuffHandle});

And control it via the session:

'/myroute': function(bar) {
   Session.set('bar', bar);
   return 'barTemplate';
 }
Tom Coleman
  • 3,037
  • 18
  • 16
  • Thanks for your prompt answer Tom! Just one thing: Might be a noob question but WHERE in my code do I put the Deps.autorun()? In my "template specific" js file just above all the template helpers and events? I think that actually matters because otherwise I will have the problem of the router stuff "happening too late" again I think?! – Patrick DaVader Aug 07 '13 at 01:18
  • another thing: Where do I set Session.set('bar')? Inside the route before returning the template name right? – Patrick DaVader Aug 07 '13 at 01:20
  • Usually I put all my subscriptions together in a single file so it's clear what data is available when. – Tom Coleman Aug 07 '13 at 01:22
  • another problem here: I can't access stuffHandle in my template helper. it is undefined there. looks like a scope problem. What am I doing wrong? I followed your tip and put my single one Desp.autorun() inside a file called "subscriptions.js" inside my client folder. Is this right? How can I fix this scope problem? – Patrick DaVader Aug 07 '13 at 03:46
  • K I think I got it to work. I now put seperate Deps.autorun()'s per view.js file. putting all my subscriptions into one Deps.autorun() inside a subsriptions.js file does not work because of the variable scope of meteor. I can not access the var's from another file, even without the "var stuffhandle". Thanks a lot for your help though! :-) – Patrick DaVader Aug 07 '13 at 05:50
0

First off, you may be missing the actual function name for the callback as demonstrated in this post.

Meteor.subscribe("myCollection", bar, function onComplete() {
    Session.set('stuffLoaded', true);
});

Which seems to be great practice. I don't usually miss a beat using this method.

Secondly, I'm not sure subscriptions inside routes work well? I'd rather do the following:

'/myroute': function(bar) {
    Session.set("myCollectionParam", bar)
    return 'stuffPage';
}

So then the subsciption finally looks like this:

 Meteor.subscribe("myCollection", Session.get("myCollectionParam"), function onComplete() {
    Session.set('stuffLoaded', true);
 });

OR (not sure which works correctly for you, depending on your publish function):

 Meteor.subscribe("myCollection", {bar: Session.get("myCollectionParam")}, function onComplete() {
    Session.set('stuffLoaded', true);
 });

Good luck!

EDIT

Just mentioning something about the publish function:

While Session.get("myCollectionParam") could return null, you can ensure the behaviour a bit more by using the following publish method:

Meteor.publish("myCollection", function(myCollectionParam) {
   check(myCollectionParam, String); 
   return MyCollection.find({_id: myCollectionParam});
});
Community
  • 1
  • 1
Seth Malaki
  • 4,436
  • 23
  • 48
  • So I had it exactly like this at first: I would set a Session var in the router, get() it inside Deps.autorun() and do the subscribe. But it didn't work properly. For some reason the subscribed publish function always returned the whole collection and NOT only the current one where foo was equal to Session.get('myCollectionParam'). I then came up with the idea to do the subscriptions in the router itself which works, except for the problem described in my first post. – Patrick DaVader Aug 07 '13 at 00:37
  • Just curious, what does your publish function look like, anyway? – Seth Malaki Aug 07 '13 at 01:59