2
votes

My app consists of two pages, Homepage and Dashboard. On the Homepage, I want to show Login (top nav) and Register (main body). If the user is logged in then show the Dashboard. On the Dashboard I want to show Logout option. On logout show the Homepage.

Problem: When I try to update the Dashboard template, all of the content ends up going directly into the navbar. What is equally confusing is the body content from the Homepage page is still being rendered in the Dashboard page. I'm obviously overlooking something but I can't tell if it's an error in my code or in my understanding of templates and routes.

In use: Iron-Router, Accounts-UI and Accounts-password, Twbs:bootstrap.

Inserted below are the three main templates Master, Homepage and Dashboard.

Thanks in advance for any help you're able to provide!

Master template with if logged in logic

<template name="MasterLayout">
    {{> yield}}
    <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">Toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="#">THE BRAND</a>
      </div>
      <div id="navbar" class="navbar-collapse collapse">
        {{#if currentUser}}
          {{> Dashboard}}
        {{else}}
          {{> Login}}
        {{/if}}
      </div><!--/.navbar-collapse -->
    </div>
  </nav>
</template>

Homepage holds the main content

<template name="Homepage">
  <div class="jumbotron">
    <div class="container">
      <h1>Get it now..</h1>
      <p>Donec id elit</p>
      {{#if currentUser}}
        hello
      {{else}}
        {{> Register}}
      {{/if}}
    </div>
  </div>
</template>

Dashboard holds a placeholder text

<template name="Dashboard">
  <ul class="nav navbar-nav">
    <li class="navbar-text">Navigation Placeholder</li>
    <li><a href="#" class="logout">Logout</a></li>
  </ul>
</template>

I created the following controllers;

Homepage

HomepageController = RouteController.extend({
  layoutTemplate: 'MasterLayout',

  subscriptions: function() {
  },

  action: function() {
    this.render('Homepage');
  }
});

Dashboard

DashboardController = RouteController.extend({
  layoutTemplate: 'Dashboard',

  subscriptions: function () {
  },

  data: function () {
  },

  action: function () {
    this.render('Dashboard', { /* data: {} */});
  }
});
2

2 Answers

2
votes

There are quite a few issues with the way that you have your route controllers defined, but no problem with that. I experienced the same confusion about how to properly set them up when I first started using Iron-Router, but hopefully after I offer my suggestions things become much more clear for you like they did for me after I worked with Iron-Router for a bit.

First of all, since it appears that you only intend on using the one layout template named MasterLayout, I would suggest that you define the layout template for your application at a global level like so:

    Router.configure({
        layoutTemplate: 'MasterLayout'
    });

This ensures that the MasterLayout template is used for all routes and the template defined for whichever route the user is currently at will be rendered in that template's yield region. As for how to properly construct the MasterLayout template, since you are using a Bootstrap navbar construct, I would suggest structuring your MasterLayout template like so:

    <template name="MasterLayout">
        <header>
            <nav class="navbar navbar-inverse navbar-fixed-top">
                <div class="container">
                    <div class="navbar-header">
                        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                            <span class="sr-only">Toggle navigation</span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                            <span class="icon-bar"></span>
                        </button>
                        <a class="navbar-brand" href="#">THE BRAND</a>
                    </div>
                    <div id="navbar" class="navbar-collapse collapse">
                        {{> loginButtons align="right"}}
                    </div>
                </div>
            </nav>
        </header>
        <article>
            {{> yield}}
        </article>
    </template>

I suggest putting the entire navbar construct into a <header> element while you put the yield region for the layout template in an article element. Really, you can put these two different parts into any type of tag you want, but I suggest going about it this way so as to properly utilize HTML5 tags and separate the page appropriately.

As mentioned before, the yield region is where the current route's template will be inserted. Also, in order to achieve what you are aiming for with the login/logout buttons, simply place the {{> loginButtons}} template (provided by the Accounts-UI package) reference in the navbar construct as well. It will react to whether or not the current user is logged in or logged out and display the appropriate buttons with text accordingly. If you would prefer to align the login buttons to the right side of the navbar construct like I prefer, you can add the navbar-right class to the <ul> element as well as add the align="right" attribute to the {{> loginButtons}} template reference. This ensures that the login buttons will open to the left of the button and on the page rather than to the right and off the page when the buttons are pulled all the way to the right edge of the page.

In order to achieve the behavior that you have specified above, I would further suggest defining two routes, one for the home page and one for the dashboard, like so:

    Router.route('/', {
        name: 'Homepage',
        template: 'Homepage'
    });

    Router.route('/dashboard', {
        name: 'Dashboard',
        template: 'Dashboard'
    });

With these routes defined, you can then define the two templates that they refer to in their template option (plus possible subtemplates) like so:

    <template name="Homepage">
        ...
        {{> Register}}
        ...
    </template>

    <template name="Dashboard">
        ...
    </template>

    <template name="Register">
        ...
    </template>

Honestly, you do not really need to define the Register template in this case because it is already defined in the separate Homepage template and anything that you were thinking about putting in the Register template you can just put directly into the Homepage template and save the complication. These two routes and their associated templates will provide the two different pages that you specified above.

With this work done, all that there is left to do is create the appropriate Javascript like so:

    Router.onBeforeAction(function() {
        if(!Meteor.user()) {
            this.redirect('Homepage');
        }  else {
            this.next();
        }
    });

    if(Meteor.isServer) {
        Accounts.onLogin({
            Router.go('Dashboard');
        });
    }

The first part of the code redirects the user to the Homepage route if they logout, while the second part of the code redirects the user to the Dashboard route when they login.

In reality, you really do not need to utilize the route controllers like you do above. Since you only have the two routes you can easily get away with just defining the two routes like I have and add the appropriate options as necessary.

Just in case you haven't found it yet, a great reference for Iron-Router can be found here.

1
votes
  1. In your DashboardController change layoutTemplate: 'Dashboard', to layoutTemplate: 'MasterLayout',.

  2. {{> Dashboard}} will render the Dashboard template there. Then when you call this.render('Dashboard') that will also render the Dashboard template in place of {{> yield}}. You are telling it to render the same template in 2 places. So in your in your DashboardController change this.render('Dashboard', { /* data: {} */}); to this.render('Homepage', { /* data: {} */});.