Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redirecting not logged-in users with iron-router... Again

I am fighting with the common need for redirecting a user to a login page if he is not logged-in (Meteor v0.8.0 on Windows 7).

There are several similar questions on stackoverflow, but no answer seems to work for me.

Won't work #1: render()

From the documentation:

onBeforeAction: function () {
  if (!Meteor.user()) {
    // render the login template but keep the url in the browser the same
    this.render('login');

    // stop the rest of the before hooks and the action function 
    this.stop();
  }
},

Two issues here:

1- The documentation is outdated. There is no this.stop() function anymore. As stated here, the code should be :

onBeforeAction: function (pause) {
  if (!Meteor.user()) {
    // render the login template but keep the url in the browser the same
    this.render('login');

    // stop the rest of the before hooks and the action function 
    pause();
  }
},

2- This works only if the route has no layoutTemplate. If it has one, the login template is rendered in the {{>yield}} of the layoutTemplate. This is usually not what you want for a login page.

Won't work #2: Router.go() or this.redirect()

Defining a route for the login page sounds like the natural way. You can then do:

Router.onBeforeAction(function(pause) {
    if (!Meteor.user()) {
        pause();
        Router.go('\login');
    }
}, {except: ['login']});

Or:

Router.onBeforeAction(function() {
    if (!Meteor.user())
        this.redirect('\login');
}, {except: ['login']});

But strangely, there is still an issue if the original route (before redirect) has a layoutTemplate: the /login template is rendered inside the {{yield}}. Which again is not what you usually want (and definitely not what you expect, as the /login template has no layoutTemplate defined).

I found a way to partially solve this:

Router.onBeforeAction(function() {
    if (!Meteor.user()) {
        var that = this;
        setTimeout(function() { that.redirect('\login'); }, 0);
    }
}, {except: ['login']});

Now everything is fine: the /login template renders as a clean page... Except that the layoutTemplate of the original route briefly blinks before the /login template is displayed.

Have you got this same problem?

like image 403
steph643 Avatar asked May 02 '14 13:05

steph643


3 Answers

You can put an if or unless template helper in the layout template.

{{#unless currentUser}}
  {{> loginPage}}
 {{else}}
  {{> yield}}
{{/unless}}
like image 112
LakeEffect Avatar answered Oct 12 '22 12:10

LakeEffect


Ok, so it seems that the render function on a route only renders a template into the current layout. To render a template into a different layout you have to call this.setLayout('templateName'). The one caveat seems to be that you'll need to set the layout back after login.

onBeforeAction: function(pause) {
    var routeName = this.route.name;

    if (_.include(['login'], routeName))
        return;

    if (! Meteor.userId()) {
        this.setLayout("newLayout");
        this.render('login');

        //if you have named yields it the login form
        this.render('loginForm', {to:"formRegion"});

        //and finally call the pause() to prevent further actions from running
        pause();
    }else{
        this.setLayout(this.lookupLayoutTemplate());
    }
}

You could also just render the login template as the layout if your login template is all you need by calling this.setLayout('login')

like image 24
Kelly Copley Avatar answered Oct 12 '22 12:10

Kelly Copley


It looks like this has something to do with waiting on subscriptions in waitOn.

The following solves the layout rendering issues for me:

Router.onBeforeAction(function() {
    if (!Meteor.user() && this.ready())
        return this.redirect('/login');
}, {except: ['login']}); 
like image 3
Eelco Avatar answered Oct 12 '22 13:10

Eelco