Search code examples
javascriptmongodbmeteorspacebars

Is it possible to switch collection focus in Meteor spacebars templates?


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.


Solution

  • 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.