"Object is not a function" in a Meteor route - meteor

I just created two routes that work just fine, but I'm getting an odd error in the console that I would like to fix.
Exception in callback of async function: TypeError: object is not a function
at OnBeforeActions.loginRequired (http://localhost:3000/client/router/config.js?8cea1a53d7ab131377c2c4f91d534123cba79b70:12:20)
This error shows up every time I visit the same page.
This is my config.js file:
Router.configure({
layoutTemplate: "uMain"
});
var OnBeforeActions = {
loginRequired: function (pause) {
"use strict";
if (!Meteor.userId()) {
this.render("uLogin");
return pause();
} else {
this.next();
}
}
};
Router.onBeforeAction(OnBeforeActions.loginRequired, {
except: ["uLogin"]
});
The idea is to redirected all user who are not logged in to "uLogin".
It works (or I haven't found any bugs so far).
What am I doing wrong?

You can see the line where you have the error in developers console when you click on link http://localhost:3000/client/router/config.js?8cea1a53d7ab131377c2c4f91d534123cba79b70:12:20 in your console.
Your problem is that new Iron Router does not use pause() anymore. Remove pause from your onBeforeAction.
Developers console is your good friend. Learn how to use it.

Related

Iron router onBeforeActions is redirecting to startpage

I use onBeforeActions to check if users are logged in, if their profile is incomplete or disabled. This seems to work correctly.
But now the problem is whenever I directly go to a page within the app, I get redirected to startPage as well. I debugged and discovered 'user' is undefined, although I'm logged in. I use accounts-fb & -tw and account-ui packages.
How can I make sure the user is logged in? I don't understand the correct timing of the functions..
edit:
Locally I don't always seem to have this problem. user is often defined. But on my production server (I just uploaded it with a debugger statement, yes I have no testserver :/) it always seems to be undefined and forwarding me to the startPage..
router.js:
if (Meteor.isClient) {
// only for client, else the serverside router will try to run this onBeforeAction
var onBeforeActions;
onBeforeActions = {
loginRequired: function() {
var user = Meteor.user();
if (!user) {
Router.go('startPage');
} else {
if (user && user.profile && user.profile.incomplete) {
Router.go('completeSignup');
}
if (user && user.profile && user.profile.disable) {
Router.go('profileEdit');
}
}
this.next();
}
};
Router.onBeforeAction(onBeforeActions.loginRequired);
Router.configure({
notFoundTemplate: 'notFound',
layoutTemplate: 'layoutMain',
loadingTemplate: 'loading',
trackPageView: true
});
Router.map ({});
}
Is the user in the middle of logging in? What does Meteor.loggingIn() return? Here is my login check using Iron Router in Coffeescript.
requireLogin = ->
if not Meteor.user()
if Meteor.loggingIn()
this.render this.loadingTemplate
else
this.render 'accessDenied'
else
this.next()
Try something simple like this, and then add the little bits of what you want to do after. But I'm fairly sure Meteor.loggingIn() is what you're looking for here.
Here is the documentation on it: http://docs.meteor.com/#/full/meteor_loggingin
EDIT: It probably works locally because there isn't any latency so if you're logged in it can run everything immediately. If that makes sense.

Correct way to manage routes after sign in for different roles

I have 2 roles, admins and users. I have a home route '/' that is just a sign in page. When admin users sign in they must go to one route ('adminPortal') and when user users log in they must go to the 'userPortal' route. On signing out both roles should route back to '/'.
Before I had an admin role, I was routing on sign in like so:
Router.onBeforeAction(function() {
this.render('loading');
if (! Meteor.userId()) {
this.render('Home');
} else {
this.next();
}
});
which worked fine (actually it was breaking my waitOn: render loading template stuff which I just discovered but more on that later). I then added roles like this (from a Stack Overflow answer I can't find right now):
server/
insertUsers=function(){
var adminId=Accounts.createUser({
username:"admin",
password:"password"
});
Roles.addUsersToRoles(adminId,"admin");
var userIdd = Accounts.createUser({
username:"user",
password:"password"
});
Roles.addUsersToRoles(userIdd,"user");
};
and
Meteor.startup(function () {
// always start from scratch (you will want to comment this line at some point !)
Meteor.users.remove({});
if(Meteor.users.find().count()===0){
insertUsers();
}
})
and
Meteor.publish("user", function () {
if (this.userId) {
return Meteor.users.find({_id: this.userId},
{fields: {'roles': 1}});
} else {
this.ready();
}
});
And I tried to route to the user/admin portals like this:
router.js
Router.route('/', {
before: function() {
if (! Meteor.userId()) { // I acutally added this check that the user is not logged in after the infinite loading problem but I thought the question was getting too long so I just left it in rather
this.render('Home')
} else {
if (Roles.userIsInRole(Meteor.user(), 'admin')) {
Router.go('/admin')
} else {
Router.go('/user')
}
}
},
waitOn: function () {
return Meteor.subscribe('user');
}
});
Now this very almost works! If I log is as either user I go to the right portal. However, when I sign out, my onBeforeAction (i.e. first code block in this question) only renders the Home template and does not actually change the URL to '/' (i.e. the URL remains either '/user' or '/admin'). Now when I try log in a second time, it will always take me to the route that I was taken to on the first log in unless I manually change the browser URL to '/'. So I thought I'd just replace the this.render('Home') with a Router.go('/'); but that seems to have created some sort of infinite loop where the Home template never renders (it did incidentally now for the first time correctly render my loading template though).
So thanks for reading all that! What's the right way to do this?
Try adding Router.go('/'); in your logout button event, along with Meteor.logout();
Example:
Template.loginButtons.events({
'click #login-buttons-logout' : function (event, template) {
Meteor.logout(function(err) {
Router.go('/');
});
}
});
I had the same issue as you, and that was the simplest way I've found to return to home page after logout.

Better handle logged_in and logged_out without showing wrong layout in iron-router

i'm trying to get a clean redirection to the login form if the user isn't logged in. This basically works but i often see my main layout which only logged in users should see. I only see it for a second then before the login screen is showing but i'm trying to get rid of this shortly "flickering" faulty page.
My router.js looks a bit like this:
Router.configure({
layoutTemplate: 'layoutPrimary',
loadingTemplate: 'loading',
waitOn: function() {
if (Meteor.user()) {
return Meteor.subscribe('messages');
}
else {
return;
}
}
});
Router.route('/', {name: 'dashboard'});
Router.route('/login', {name: 'login'});
some more routes without anything special (some subscriptions). Now we check if the user is logged in. If not he shall be delivered a special layout containing only the login/register functions.
// require login on all routes
Router.onBeforeAction(function () {
if(!Meteor.user() && !Meteor.loggingIn()){
this.layout('layoutSlim');
this.render('login');
} else {
this.next();
}
});
then we're handling 404s and doing some functions for user check that are used in the routes above.
// 404 Handling
Router.route('/(.*)',function(){
this.render('error404');
});
// Be sure the Meteor.user() exists when settings are loaded
var userIsLoaded = {
ready: function() { return !!Meteor.user(); }
};
var userLoggedIn = function() {
if (!Meteor.user() && !Meteor.loggingIn()) {
this.stop();
Router.go('login');
return false;
}
else {
return true;
}
};
Any idea what i can do to avoid that shortly showing wrong layout? I also sometimes see the login screen when logged in - this is quite rare but it would be a better user experience if this wouldn't happen at all.
I'm not 100% sure if this is related to iron-router or probably an issue that could be handled by spacebars/blaze?
Thanks for helping,
Frank
This is happening because the subscribe('messages') gets done, but the Meteor users collection not.
You can use the currentUser helper from Accounts Package
{{if currentUser}}
<!-- show information here -->
{{else}}
<!-- Forbiden template or login template -->
{{/if}}

Meteor Iron Router goes to a route's waitOn first instead of Router.onBeforeAction

I've got the Meteor Roles package and I'm trying to define an admin route:
var requireLogin = function() {
if (! Meteor.user()) {
debugger // #1
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
console.log("no user");
this.render('AdminLogin');
}
} else {
this.next();
}
};
Router.onBeforeAction(requireLogin, {only: ['AdminMain']});
Router.route('/admin', {
name: 'AdminMain',
layoutTemplate: 'AdminLayout',
waitOn: function(){
debugger // #2
return [
Meteor.subscribe("appointments")
]
}
});
I've got this in server/publications:
Meteor.publish('appointments', function() {
if (Roles.userIsInRole(this.userId, ['assistant','admin'])) {
return Appointments.find();
} else {
console.log("no user");
return [];
}
});
The first debugger that gets set off is debugger #2 in the waitOn. Why? I have an OnBeforeAction specified precisely for that route. According to the Iron Router guide, Our onBeforeAction hook function will run before our route function when the user navigates to "/admin". If the user is not logged in, the route function will never get called and the AdminPage will not render to the page.
Well, it certainly looks like the route function is being called before the OnBeforeAction considering that debugger stops first to waitOn for a Meteor subscription. Since this subscription requires an admin user to be logged in on the server, if I press continue on debugger the server console logs "no user" and the loading screen goes on forever and forever. The actual OnBeforeAction function for requireLogin never gets called.
waitOn is called before OnBeforeAction. This behavious is corect. From the iron-router docs:
Another alternative is to use waitOn instead of subscribe. This has the same effect but automatically short-circuits your route action and any before hooks (see below), and renders a loadingTemplate instead.
Source
For handling subscriptions you can use 'subscriptions' option:
Router.route('/post/:_id', {
subscriptions: function() {
// returning a subscription handle or an array of subscription handles
// adds them to the wait list.
return Meteor.subscribe('item', this.params._id);
},
action: function () {
if (this.ready()) {
this.render();
} else {
this.render('Loading');
}
}
});
Source

How to properly replace this.stop() with pause() on Iron Router blaze integration

When I upgrade Iron Router to blaze integration branch, I began receiving this warning:
"You called this.stop() inside a hook or your action function but you should use pause() now instead"
Chrome console --> iron-router.js:2104 --> client/route_controller.js:193 from package
The code is on client:
Router.before(mustBeSignedIn, {except: ['userSignin', 'userSignup', 'home']});
var mustBeSignedIn = function () {
if (!Meteor.user()) {
// render the home template
this.redirect('home');
// stop the rest of the before hooks and the action function
this.stop();
return false;
}
return true;
}
I tried replacing this.stop() with: pause(), Router.pause() and this.pause() but still does not work. Also I haven't found pause function on iron-router package.
How do I properly replace this.stop() with pause()?
Thanks
From what I can tell the pause function is the first parameter your before hook is getting called with. Not in the docs anywhere, but that's what I gathered from the code and it seems to work.
Here's what I use:
var subscribeAllPlanItems = function (pause) {
var planId = this.params._id;
this.subscribe('revenues', planId).wait();
this.subscribe('expenses', planId).wait();
};
var waitForSubscriptions = function (pause) {
if (this.ready()) { //all the subs have come in
//NProgress.done();
setPlan(this.params._id);
} else { //all subscriptions aren't yet ready, keep waiting
//NProgress.start();
pause();
}
};
Router.map(function () {
this.route('calendar', {
path: '/calendar/:_id',
template: 'calendar',
before: [
subscribeAllPlanItems,
waitForSubscriptions
],
});
//Other routes omitted
});
var requireLogin = function (pause) {
if (Meteor.loggingIn()) { //still logging in
pause();
}
if (!Meteor.user()) { //not logged in
this.render('signIn');
pause();
} else { //logged in, life is good
console.log("requireLogin: logged in");
}
};
//This enforces login for all pages except the below ones.
Router.before(requireLogin, {
except: ['landing', 'signUp', 'signIn', 'forgotPassword', 'resetPassword']
});
I opened an issue on Github about this. Here's the response I got:
Oops I may have not changed the redirect method yet. Just use Router.go as it will work fine now. I will change over this.redirect sometime next week or a PR is welcome. Controllers are now automatically stopped if you change routes in a hook. You can pause the current run by calling the pause method which is passed as a parameter to your hooks and action functions.

Resources