Reputation: 14769
I have a template helper like so
Template.index.allUpcomingGames = function() {
return Games.find({date: {$gte: new Date()}}, {sort: {date: 1}});
};
So if I'm reading the docs right the template should be re-rendered whenever this query result changes.
The actual behaviour I'm seeing is that when i first load the page (empty minimongo cache) the template renders no results. When an incremental change is detected and the template is forcibly rerendered the items magically appear, so presumably the initial page load is happening before the query returns, and subsequently not reactively re-rendering as I would expect. What am I doing wrong? What is best practice for managing template dependencies on queries?
Should have included pub/subs. Very simple I'm just publishing everything at the minute although once this is working it will get smarter. I have removed autopublish package.
publish
Meteor.publish("allGames", function(userId) {
return Games.find();
});
subscribe
Meteor.subscribe("allGames", this.userId);
As you can see I'm not using the userId yet
I've simplified this to something reproducible, it looks like there's a fundamental misunderstanding about collections on my part. I've put together 3 test cases here:
https://github.com/antiBaconMachine/meteorCollectionRenderingTests
The output I see for the 3 tests is
I would expect the first test to render reactively as forEach
should fetch the documents which I had assumed was being called by handlebars {{#each}}
.From the meteor docs:
Cursors are a reactive data source. The first time you retrieve a cursor's documents with fetch, map, or forEach inside a reactive computation (eg, a template or autorun), Meteor will register a dependency on the underlying data.
The second test works as I would expect which confuses me as the only difference is that I retrieve the count.
I'm assuming the last test fails as I'm fetching the documents before they are ready and once retrieved the query is no longer reactive? That is just a guess.
Any insight into this would be appreciated I'm a bit lost.
Upvotes: 1
Views: 247
Reputation: 6676
Note: the initial answer and the bug reproduction that followed it are completely different. The one helped to spot another. Looks for the second part of this answer for real information.
Answer to the initial question:
This behavior makes sense to me:
Let's look at your query:
Games.find({date: {$gte: new Date()}}, {sort: {date: 1}})
You are looking for all games, sorted by date whose date is greater than the date the query is created.
So let's say you have following items in the database:
A, date: 100
B, date: 200
C, date: 300
Your client js is loaded and the current time is 350
. Your query is created as Games.find({ date: {$gte: 350} }, {sort: {date: 1}});
. Surely A, B and C are behind 350
date and thus are not rendered.
Then you insert a new game into database, say D, date: 400
. Yes, it should appear be returned by the query that looks at everything created after 350
.
What behavior do you expect?
Additions to updates:
This is a bug.
This bug was filed for the shark rendering engine. New engine (spark) has this issue solved and will be released in the next Meteor release. (maybe 0.7)
The bug happens when you pass a minimongo cursor as a template data context. It's very unusual way to get data to templates. I think that's why it existed for so long but was not spotted.
The temporary workaround is to not pass cursors as data context. Instead you can pass id or name or anything else and then have a separate template helper for callee template that returns an appropriate cursor.
A funny thing: instead of passing cursor to a template you can embed it into #with block helper and it will work as expected:
{{#with games}}
<ul>
{{#if this.count}}
{{#each this}}
<li class="u-margin--sm">
<a href="/game/show/{{_id}}" class="col-md-8">{{name}}</a>
</li>
{{/each}}
{{else}}
<span class="t-small">No games</span>
{{/if}}
</ul>
{{/with}}
And everything works :).
Re: withFetch
It works as expected if you use length
instead of count
. When you fetch, cursor returns a plain JS array, it doesn't have a count
property but has length
.
Upvotes: 2