6

Why in the following basic example returned collection inside rendered function is empty?
Autopublish is enabled. After the page loads calling command
Coll.find().fetch() inside javascript console returns correct set of entries

Here is the code

t.js

Coll = new  Meteor.Collection("coll");

if (Meteor.isClient) {
  Template.tpl.rendered = function(){
    console.log(Coll.find().fetch()); // <-- This line prints empty array
  };
}

if (Meteor.isServer) {
  Meteor.startup(function () {
      if (Coll.find().count() === 0) {
          var f = ["foo","bar"];
          for (var i = 0; i < f.length; i++)
              Coll.insert({f: f[i]});
      }
  });
}

And t.html file

<head>
  <title>test</title>
</head>

<body>
  {{> tpl}}
</body>

<template name="tpl">
  Test tpl
</template>
Freakyuser
  • 2,774
  • 17
  • 45
  • 72
Elrot
  • 253
  • 1
  • 3
  • 8
  • It's because your collection is not loaded yet. `Template.rendered ` is fired, doesn't mean that your collection is loaded. check [this](http://stackoverflow.com/questions/15129827/) thread. – sohel khalifa May 01 '13 at 08:09

1 Answers1

5

Meteor is built off of a data-on-the wire type structure. This means when the app initially loads the HTML & JS is sent down first and the data later.

You have to use reactivity to check for data changes or check when the subscription to a collection is complete (which entails removing the autopublish package). (You can check out how to move your app to a manual subscription at the docs: http://docs.meteor.com/#publishandsubscribe)

The subscription callback tells you when the data is returned:

Meteor.subscribe("coll", function() {
    //Data subscription complete. All data is downloaded
});

A template can also be made reactive (like the way you are doing) but .rendered isnt being called because Meteor first checks to see if a template's html has changed & only if it is different will it change its HTML and call the rendered callback.

What you have as an option here is to 1) use Deps.autorun instead, or

2) I'm not sure why you are using this in your rendered callback but if it is necessary to put it there you need to ensure that the HTML of the template changes, by introducing something into the html from your collection that makes it change when the new data is introduced.

Tarang
  • 75,157
  • 39
  • 215
  • 276
  • I'm stuck here too. Can you elaborate on this? I'm trying to manually render a template that generates an c3 js chart and am trying to load data from the database but getting the empty array as above. – radtek Oct 21 '14 at 03:00
  • @radtek Meteor works by sending the html first, then the data later. When your rendered callback fires, the data may not or may have been sent. You have to so something to make sure you wait for it to arrive using one of the above methods or iron router's `subscribe('..').wait()` command before you draw your chart. – Tarang Oct 21 '14 at 04:11
  • Yeah that much I know. I guess what I am after is an example of the c3 js meteor package in use. Works fine with static data but there should be a best practices example for working with db data. I'll see what I come up with later tonight. When I tried to render the html then subscribe and put out the data via handlebars, I tried to generate a js script inside the template itself but handlebars would create a new line when I did a for each so it wasn't even parseable js. var data = []; data.push({{each_item}}); . So then I thought this is way too hacky and there needs to be a better way – radtek Oct 21 '14 at 14:39
  • Here is the c3 js package: https://atmospherejs.com/peernohell/c3, here is another, at least it has an example in this one: https://atmospherejs.com/maazalik/highcharts . But who would use it in such a way with static data? I'm sure people want to use db data. – radtek Oct 21 '14 at 14:42
  • @radtek. You can use db data! Just use the callback of the subscription function like i've done above, or a `Deps.autorun/Tracker.autorun`. The idea is essentially that you draw the chart once the data has arrived. The callback above for the `Meteor.subscribe` fires when the data has arrive so you can create some logic to run the chart only after this has fired. If you use iron router's `subscribe.wait()` then you can just use your .rendered function as is since it will display a loading template (if you set it) until the data has arrived, nothing more. its nearly a one line of code fix. – Tarang Oct 21 '14 at 15:23
  • Thanks! I currently have my subscribe at the top of the client.js file (I'm using the /client /server dirs hierarchy). I'll give that a shot. – radtek Oct 21 '14 at 18:19
  • Ok so wrapping it in the subscribe didn't work for me, still blank. My repo is here: https://github.com/radlws/meteor-ratings-app . I just started playing with meteor Friday so still new to it but I did remove insecure and autopublish long ago from the app. If i figure it out I'll post a more dummy proof answer. – radtek Oct 21 '14 at 21:28
  • I used Meteor.subscribe("col").ready callback and it seemed to do the trick. I switched to highcharts because had better doc but on data refresh the chart isn't redrawn yet the code (helper) that sets it all up is called. So have to find a way to destroy old and redraw new.. off topic. – radtek Oct 22 '14 at 00:56