We’re pushing forward in our quest to slim down our router code. After getting rid of subscriptions and data context declarations, it’s time to take on redirects.

Thinking About Redirects

A “traditional” redirect sends the user to a new route, but a better and less disruptive pattern is to keep the URL the same, and instead just switch out the contents of the template. This type of “in-place” redirect is what we’ll use.

Another factor to consider is that most redirects rely on subscription data –typically the current user– to do their job: after all, you can’t know whether to let someone through or not until you know who they are.

Redirecting commonly happens when a user tries to access a route without the proper rights. In Iron Router it’s done with filters, route-level callbacks that can intercept a routing call and change its result.

For example, here’s what the isLoggedIn filter originally looked like (warning: ugly code ahead):

filters.isLoggedIn = function() {
  if(Meteor.loggingIn()){
    this.render('loading');
  }else if (!Meteor.user()) {
    if (this.ready()) {
      this.layout('formPageLayout')
      this.render('loginPage');
    }else{
      this.render('loading')
    }
  } else {
    this.next();
  }
}

Router.onBeforeAction(filters.isLoggedIn, {only:
  ['fullBook', 'video', 'interview']
});

As you can see, a lot of the complexity comes from the fact that the filter relies on subscription data from the Meteor.user() object.

But wait, didn’t we just spend the previous two episodes getting subscriptions out of the router and into templates? This leads to the conclusion that redirects also need to migrate out of the router.

Template-Level Layouts

Redirecting at the template level works great if all we’re doing is replacing part of a template with, say, a log-in form:

<template name="foo">
  {{#if loggedIn}}
    <!-- show foo contents -->
  {{else}}
    {{> loginForm}}
  {{/if}}
</template>

But what if we want to not only replace part of the template when the user isn’t logged in, but also switch out the whole layout wrapping the template as well?

To see what I mean, head to one of our videos while logged out. You’ll see that the outer layout looks quite different from the default one you get on the table of contents

You guessed it: layouts, too, need to be kicked out of the router down to the template level. A naive implementation would probably look something like this:

<template name="foo">
  {{#if loggedIn}}
    <div class="default-layout">
      <div class="foo">
        <!-- foo contents -->
      </div>
    </div>
  {{else}}
    <div class="form-layout">
      <div class="bar">
        {{> loginForm}}
      </div>
    </div>
  {{/if}}
</template>

Wrapper Templates

Now it would be a pain to repeat the same wrapper markup in every single template; but thankfully we don’t have to thanks to wrapper templates.

Here’s how they work. First, define a new template with a special {{> Template.contentBlock}} include inside it:

<template name="defaultLayout">
  <div class="default-layout">
  <div class="foo">
    {{> Template.contentBlock}}
  </div>
</div>
</template>

Then just use that template as a block helper in any other template!

<template name="foo">
  {{#if loggedIn}}
    {{#defaultLayout}}
      <!-- foo contents -->
    {{/defaultLayout}}
  {{else}}
    {{#formLayout}}
      {{> loginForm}}
    {{/formLayout}}
  {{/if}}
</template>

Wrapperception

As the Meteor Guide mentions, you can use this technique not just for layouts, but also for permissions checks:

<template name="forceLoggedIn">
  {{#if currentUser}}
    {{> Template.contentBlock}}
  {{else}}
    {{#formLayout}}
      {{> loginForm}}
    {{/formLayout}}
  {{/if}}
</template>

This lets us simplify our foo template quite a bit:

<template name="foo">
  {{#forceLoggedIn}}
    {{#defaultLayout}}
      <!-- foo contents -->
    {{/defaultLayout}}
  {{/forceLoggedIn}}
</template>

Yep, we’re wrapping our wrappers inside other wrappers!

Slimming Down

As you can see, I’m taking more and more logic out of the router in an effort to make the codebase router-agnostic and facilitate the transition to FlowRouter.

This does lead to repeating a lot of the same code in multiple templates, but over time I’ve come to the conclusion that it’s sometimes better to be a little more explicit, even at the cost of “wetter” code!