I am trying to display some relational data in a Meteor spacebars template. Specifically I two collections, Location and Weather. They look something like this:
Location {
_id: 'full-name',
title: 'Full name',
lat: '123.456',
long: '123.456',
order: 1
}
Weather {
locName: 'full-name', // this name always matches a location _id
temperature: '41.3'
}
I'd like to display information from both of these collections on a single page. So that I can show the latest weather from each location (there are 4-20 of them per page). To do this, I've published a Mongo request of both collections like so on the server side:
Meteor.publish('allLocations', function() {
return [
Locations.find({}, { sort: { order: 1 } }),
Weather.find({}) // The weather
]
});
I then subscribe to this publication in my router (iron-router):
Router.map(function() {
this.route('locations', {
waitOn: function () {
return Meteor.subscribe('allLocations');
}
}
});
However, I get stuck when I get to my spacebars template. I can't figure out the syntax for switching collection focus in spacebars.
Here's the psuedo-code for the template that I'm trying to parse, but I know that this doesn't currently work.
<template name="locations">
<div class="locations-grid">
{{#each locations}}
<div class="location {{_id}}">
This is the location template
<h1>{{title}}</h1>
{{#each weather}}
<!-- Trying to pass the _id along to the weather template for filtering -->
{{> weather _id}}
{{/each}}
</div>
{{/each}}
</div>
</template>
<template name="weather">
This is the weather template
{{#with weather}}
<!-- Ideally, we've now switched contexts to the weather collection -->
<h2>Temperature: <div class="temp">{{temperature}}</div></h2>
{{/with}}
</template>
So my question is, where do I tell spacebars to switch contexts to the weather collection? How can I pass along the _id variable to the weather template so that I can select the right data from the collection? I know I'm missing a big step here, I just can't figure out which portion of the Meteor space to examine. I know I might need to specify a subscription for the weather template, but I'm not sure where to do that since it's not really a route, since it won't have its own page. It just lives as a sub-template within the locations template.
Thanks for any tips, or possible suggestions on restructuring.
Before we begin, please read A Guide to Meteor Templates & Data Contexts - that will correctly orient you on contexts inside of #each
blocks.
Your goal is to join the correct weather
documents to their corresponding location
documents. This is most easily accomplished by introducing sub-templates for both types. Let's begin with the top-level template:
<template name="locations">
<div class="locations-grid">
{{#each locations}}
{{> location}}
{{/each}}
</div>
</template>
Which has a locations
helper like this:
Template.locations.helpers({
locations: function() {
return Locations.find();
}
});
Next, the location
template:
<template name="location">
<div class="location">
<h1>{{title}}</h1>
{{#each weathers}}
{{> weather}}
{{/each}}
</div>
</template>
Which has a weather
helper like this:
Template.location.helpers({
weathers: function() {
return Weather.find({locName: this._id});
}
});
The key insight here is that the context of a location
template is a single location document so weather
will return only the weather documents for this location instance. Finally, your weather template can look like:
<template name="weather">
<h2>Temperature: {{temperature}}</h2>
</template>
Note we are now in a weather
context so the #with
is no longer needed.
Side note - using sort in your publisher has no affect in this case.