meteor iron-router nested routes

iron-router, meteor, nested

Solution

After going through a whole lot of different options, I finally found an option of iron-router that I didn't notice before. This makes the magic.

The implementation of nested routes (at least with two levels (I didn't try more levels)) becomes much easier with the use of yields:

My route is this one:

Router.map ->
  @route "buildingSpaces",
    path: "/buildings/:_id/spaces"
    template: "building"
    yieldTemplates: {
      'buildingSpaces': {to: "subTemplate"}
    }
    waitOn: ->
      [subs.subscribe("allSpaces"),
       subs.subscribe "allBuildings"]

    data: ->
      building: Buildings.findOne(@params._id)
      spaces: Spaces.find({building_id: @params._id})

These are my templates:

template(name="building")
    .animated.fadeIn
        +with building
                .building
                    .page-header.position-relative
                        h1.inline #{name}
                    .row
                        .col-xs-12
                            .tabbable
                                ul.nav.nav-tabs
                                    li#menu-spaces(class="{{isActive 'buildingSpaces'}}")
                                        a(href="{{pathFor 'buildingSpaces'}}") #{nbr_spaces} Spaces
                                    li#menu-dashboards(class="{{isActive 'buildingDashboards'}}")
                                        a(href="{{pathFor 'buildingDashboards'}}") Dashboards
                                .tab-content
                                    +yield "subTemplate"


template(name="buildingSpaces")
    .animated.fadeInDown
        .page-header.position-relative
            a.btn.btn-info.btn-sm#newSpaceButton
                i.ace-icon.fa.fa-plus
                | New space
        .clearfix
        +spacesList

template(name="spacesList")
    .widgets
        .row
            +each spaces
                .col-xs-12.col-sm-6
                    .widget-box.widget-color-purple
                        .widget-header
                            a(href="{{pathFor 'space'}}") #{name}
                        .widget-body
                            p Content here
    +insertSpaceForm

And finally, I have a helper to manage my menu:

Handlebars.registerHelper "isActive", (template) ->
  currentRoute = Router.current().route.name
  if currentRoute and template is currentRoute then "active" 
  else ""

It's quite slick. It loads only the subTemplate when clicking the submenu. It's the best I found...

Hope this helps...

Problem

I have two meteor collections with a one-to-many relationship: buildings and spaces On my building page, I want to show the spaces related to the building. For now, I did it this way: ``` buildingsRoute.coffee BuildingController = RouteController.extend(template: "buildings") Router.map -> @route "building", path: "/buildings/:_id" waitOn: -> subs.subscribe "allBuildings" subs.subscribe "allSpaces" data: -> building: Buildings.findOne(@params._id) spaces: Spaces.find({building_id: @params._id}) ``` building.jade: ``` template(name="building") +with building .building .page-header.position-relative h1.inline #{name} .row .col-xs-12 +spacesList template(name="spacesList") .widgets .row h1 Spaces +each spaces .col-xs-12.col-sm-6 .widget-box.widget-color-purple .widget-header a(href="{{pathFor 'space'}}") #{name} .widget-body p Content here ``` This doesn't work as such, I guess because the data context of the spacesList template is not the same as the one defined by iron router for building. I could replace "+each spaces" by "+each ../spaces" but this doesn't seem like a very generic solution to me (what if I want to use my spaceslist template in another context?) So I tried to define the data context in template helpers like this: ``` Template.spacesList.helpers spaces: Spaces.find({building_id: @params._id}) ``` But I get the error message: ``` Spaces is not defined. ``` So I'm a bit confused. What is the meteor way to implement nested routes properly? Thank you! EDIT: Definition of Spaces collection: /models/space.coffee ``` @Spaces = new Meteor.Collection("spaces", schema: building_id: type: String label: "building_id" max: 50 name: type: String label: "Name" optional: true max: 50 creation_date: type: Date label: "Creation date" defaultValue: new Date() ) ``` Publications: /server/publications.coffee ``` # Buildings Meteor.publish "allBuildings", -> Buildings.find() Meteor.publish "todayBuildings", -> Buildings.find creation_date: $gte: moment().startOf("day").toDate() $lt: moment().add("days", 1).toDate() # Publish a single item Meteor.publish "singleBuilding", (id) -> Buildings.find id # Spaces # Publish all items Meteor.publish "allSpaces", -> Spaces.find() ``` EDIT 2 After some researches, I finally came up with a solution: ``` Template.spacesList.helpers spaces: () -> if Router._currentController.params._id subs.subscribe "buildingSpaces", Router._currentController.params._id Spaces.find() else subs.subscribe "allBuildings" Spaces.find() nbr_spaces: () -> Spaces.find().count() ``` With an additional publication: ``` # Publish all items for building Meteor.publish "buildingSpaces", (building_id) -> Spaces.find({building_id: building_id}) ``` The errors were: - the fact that the space definition wasn't wrapped into a function - the @params._id that I replaced by the not very sexy Router._currentController.params._id but I couldn't find any shortcut for this. I still don't know whether this is the Meteor (best) way to manage nested routes... Any better recommendation?

Original source