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?
You can put an if or unless template helper in the layout template.
{{#unless currentUser}}
{{> loginPage}}
{{else}}
{{> yield}}
{{/unless}}
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')
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']});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With