0

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.

bryan kennedy
  • 6,969
  • 5
  • 43
  • 64

1 Answers1

1

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.

Community
  • 1
  • 1
David Weldon
  • 63,632
  • 11
  • 148
  • 146
  • Thanks. This is very helpful. I also read through the linked post on Discover Meteor. I am still a bit stumped by one change. To do it this way I can only publish the Location collection to the location template. How can I tell the location template which collection to subscribe to? I'm used to doing that in the router, but since this location doesn't have a route, I'm not certain *where* that subscription would go. – bryan kennedy Oct 26 '14 at 18:33
  • It doesn't look like you need to change anything. Subscriptions are not specific to a template - they are a request to have the server push data to the client database (minimongo). When you navigate to the top-level template, your router subscribes to `allLocations`, so all of the weather and location data will be available to all of the templates. Does that make sense? – David Weldon Oct 26 '14 at 19:12
  • When I keep both the Location and the Weather collection in the allLocations publication the #each Weather returns `this` as the Locations object but also includes the Weather entries. – bryan kennedy Oct 26 '14 at 19:35
  • Fixed! The problem wasn't in the publication, it was with reusing `weather` as both the helper and the template name. Let me know if the updated code works for you. If not, I have a repo demonstrating all this that I can publish. – David Weldon Oct 26 '14 at 20:03
  • Huzah! That fixed it. I see the difference between weathers (the collection cursor) and weather (the sub template). Thanks very much for taking the time to explain this and hash out the details with me. – bryan kennedy Oct 26 '14 at 20:20