"Cannot read property 'username' of undefined" in a Meteor Route? - meteor

The page still renders as it should, but this appears in my console log.
Exception in callback of async function: TypeError: Cannot read
property 'username' of undefined
at null. (http://localhost:3000/router.js?39194109cc7760190c519a386d66c807ba19230c:48:9)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at onRerun (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:520:9)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at onRun (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:505:11)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at dispatch (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:448:3)
Which points to the line indicated below:
var OnBeforeActions = {
loginRequired: function() {
if (!Meteor.userId()) {
this.render('hello');
} else {
this.next();
}
}
};
Router.onBeforeAction(OnBeforeActions.loginRequired, {
only: ['home']
});
Router.route('/', function() {
// this.render('homepage');
Router.go('/users/' + Meteor.userId());
});
Router.route('/users/:user_id', function() {
this.render('home', {
data: {
username: Meteor.users.findOne({
_id: this.params.user_id
}).username, // THIS LINE HERE
}
});
});
The idea is to redirect the user to his page if he is already logged in - and it appears to work as it should, but why am I getting this?

When the browser loads initially there will not be any data available (all the collections will be empty - until they are downloaded. Thus the result of the query Meteor.users.findOne({_id: this.params.user_id}) will be undefined.
This is the reason you can't read username out of undefined.
Since the query will re-run/it is reactive you just need to take account when it is null and it will sort itself out. user.username is only read if user is not null:
Router.route('/users/:user_id', function() {
var user = Meteor.users.findOne({_id :this.params.user_id});
this.render('home', {
data: {
username: user && user.username
}
});
});

Related

How to access FlowRouter subscriptions in Meteor template helpers?

it seems like I can't access a FlowRouter template subscription in my helper. How can you do this?
In my server code:
Meteor.publish('AllUsers', function() {
return Meteor.users.find({}, {fields: {profile: 1}});
})
In my router code:
var userRoutes = FlowRouter.group({
subscriptions: function(params, queryParams) {
this.register('AllUsers', Meteor.subscribe('AllUsers'));
},
});
In my template code:
{{#if checkFlowRouterSubs}}
{{#each getTheUsers}}
{{>userPartial}}
{{/each}}
{{/if}}
In my helpers I have the 'guard':
checkFlowRouterSubs: function() {
if (FlowRouter.subsReady()) {
return true;
};
return false;
},
And then the getTheUsers helper:
...
var users = AllUsers.find(filterObject, { sort: { 'profile.firstname': 1 } }).fetch(); // the actual query definitely works
...
But I get an error:
Exception in template helper: ReferenceError: AllUsers is not defined
I should note that in the getTheUsers helper, FlowRouter.subsReady('AllUsers') returns true
so, first, this :
var userRoutes = FlowRouter.group({
subscriptions: function(params, queryParams) {
this.register('AllUsers', Meteor.subscribe('AllUsers'));
},
});
is NOT server code: it is Client code: the Flow-router is a client side router: counter intuitive but this is the basis of all these routers.
The hint here is that you are 'subscribing' to the publication in this code, so it is on the client side.
Iron-Router is routing both on the server and client-side so it makes things even more confusing when you come from there.
What you are missing here is the publish function on the server side.
Meteor.publish('AllUsers', function() {
return AllUsers.find();
});
EDIT:
The Error
Exception in template helper: ReferenceError: AllUsers is not defined
seems like because you did not define the collection on the client side
var AllUsers = Mongo.Collection('AllUsers'); //or whatever the actual collection
When you try to get data from a subscription, you want to call the actual collection you're looking to get data for, not the subscription name. In this case, I think you mean Meteor.users:
var users = Meteor.users.find(filterObject, { sort: { 'profile.firstname': 1 } });
if( users ) {
return users.fetch();
}

Setting data that belongs to a User in iron router

Router.route('/settings', {
name: 'settings',
data: function() {
return Settings.findOne({userId: Meteor.user()._id});
}
});
It's showing an error in the browser:
Uncaught TypeError: Cannot read property '_id' of undefined
Any suggestions on how to grab the settings record for a logged in user?
Meteor logging in process usually takes a few ms to get ready, meanwhile Meteor.user() will return undefined and the first execution of your route data method will fail.
You can use Meteor.userId() to avoid this from happening until the user is connected for real.
Router.route('/settings', {
name: 'settings',
data: function() {
return Settings.findOne({
userId: Meteor.userId()
});
}
});

object is not a function in iron-router routes at onBeforeAction hook's this.next()

I'm running iron-router 1.0.7, and my onBeforeAction hook is causing an exception. Here's the stack trace:
Exception in callback of async function: TypeError: object is not a function
at Router.onBeforeAction.except (http://localhost:3000/lib/router.js?ebfe803416134e7c5b16265486b3998532289c58:45:8)
at http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1199:36
at _.extend.withValue (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:955:17)
at Router.addHook.hookWithOptions (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1198:27)
at boundNext (http://localhost:3000/packages/iron_middleware-stack.js?0e0f6983a838a6516556b08e62894f89720e2c44:424:31)
at Meteor.bindEnvironment (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:983:22)
at Router.onBeforeAction.only (http://localhost:3000/lib/router.js?ebfe803416134e7c5b16265486b3998532289c58:28:12)
at http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1199:36
at _.extend.withValue (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:955:17)
at Router.addHook.hookWithOptions (http://localhost:3000/packages/iron_router.js?a427868585af16bb88b7c9996b2449aebb8dbf51:1198:27)
and here are the hooks that are referenced:
Router.onBeforeAction(function () {
//check if it's a logged in user
if (!Meteor.userId()) {
//if they aren't redirect them to login
this.render('login');
} else {
//if they are let them through
this.next();
}
},
//the routes we want this if logged in check for
{only: ['profile', 'logout', 'users']
});
// Redirects users to the survey until they have answered all the questions
Router.onBeforeAction(function() {
Meteor.subscribe('questions');
var answered = getUserAnswers();
var unanswered = Questions.find({ _id: { $nin: answered } }, {sort: {priority: 1}}).count();
if (unanswered && Roles.userIsInRole(Meteor.user(), 'manage-users')) {
this.next();
} else if (unanswered) {
this.redirect('survey');
}
this.next();
},
{except: ['survey', 'passwordReset', 'logout', 'settings', 'login']});
It seems to be the last this.next() that's causing the exception (That's the line referenced in the first line of the trace.), but I can't tell why. If I put a breakpoint on it in DevTools, it runs twice just fine. Then, on the third stop, next is undefined. Any idea why this is happening? The routes still seem to work normally despite the exception.

Meteor methods wait before execution on client proceeds

could anybody please tell me how to make clients wait until the called function on the server is executed?
My code:
Meteor.methods({
markLettersAsRead: function(userId) {
if(serverVar) {
Users.update({_id: userId}, {$set: {letters: []}}); // removing all references
}
}
});
Template.letter.events({
'click a': function() {
Meteor.call('markLettersAsRead', Meteor.userId(), this._id, function(err) {
if (err) {
console.log(err);
}
});
var usersExistsWithThisLetter = Users.find({letters: {_id: this._id}}).count();
console.log(usersExistsWithThisLetter);
}
});
In my example usersExistsWithThisLetter is always 1 because the Users.find() doesn't wait until the Meteor.call is done. I verified this by checking the database and no users exists with entries in the letters array.
Any help would be greatly appreciated.
You need to query the collection inside the callback, because then you can be certain that your server method has already been executed. I would do something like this (note the self variable declaration):
var self = this;
Meteor.call('markLettersAsRead', Meteor.userId(), this._id, function(err) {
if (!err) {
var usersExistsWithThisLetter = Users.find({letters: {_id: self._id}}).count();
console.log(usersExistsWithThisLetter);
} else {
console.log(err);
}
});
I hope it helps!

Collection2, insert using method, exception from unique constraint not caught

I create a new project:
$ mrt create sandbox
$ mrt remove autopublish
$ mrt add collection2
And use the following code to create a simple collection with a unique constraint on a key
SandBoxCollection = new Meteor.Collection('sandboxcoll', {
schema: new SimpleSchema({
title: {
type: String,
min: 3,
unique: true,
index: true
}
})
});
if (Meteor.isServer) {
Meteor.publish('sandboxpub', function() {
return SandBoxCollection.find();
});
}
if (Meteor.isClient) {
Meteor.subscribe('sandboxpub');
}
Meteor.methods({
create: function(doc) {
var docId = SandBoxCollection.insert(doc, {validationContext: 'create'}, function(err, res) {
if (err) {
throw new Meteor.Error(333, SandBoxCollection.simpleSchema().namedContext('create').invalidKeys());
}
return res;
});
return docId;
}
});
I set up a simple collection, pub/sub and a method that I can use for inserts.
Then I use the browser console to issue the following commands
Let's first create a document:
Meteor.call('create', {title: 'abcd01'}, function(e,r){
console.log(e ? e : r);
});
Now let's try inserting a duplicate directly using collection.insert():
SandBoxCollection.insert({title: 'abcd01'}, function(e,r) {
console.log('error: ');
console.log(e);
console.log('errorkeys: ');
console.log(SandBoxCollection.simpleSchema().namedContext().invalidKeys());
console.log('result: ');
console.log(r);
});
We can see a proper 333 error handled by the callback and logged to the console.
Now try inserting a duplicate using the method:
Meteor.call('create', {title: 'abcd01'}, function(e,r){
console.log(e ? e : r);
});
Notice that, unlike the direct insert, the method throws an uncaught exception! Furthermore, the error is thrown from our custom throw and it has error code 333.
Why is this not handled properly? What can I do to mitigate this so that I can do something with the error (notify the user, redirect to the original documents page etc)
As of February 2014, this is an enhancement request on collection2 issue tracker at https://github.com/aldeed/meteor-collection2/issues/59
The current workaround (on the server) is to catch the error separately and feed it into a custom Meteor.Error as in:
if (Meteor.isServer) {
Meteor.methods({
insertDocument: function(collection, document) {
check(collection, String);
check(document, Object);
var documentId = '',
invalidKeys = [];
function doInsert() {
documentId = SandboxProject.Collections[collection + 'Collection'].insert(document, {validationContext: collection + 'Context'});
}
try {
doInsert();
} catch (error) {
invalidKeys = SandboxProject.Collections[collection + 'Collection'].simpleSchema().namedContext(collection + 'Context').invalidKeys();
error.invalidKeys = invalidKeys;
throw new Meteor.Error(333, error);
}
return documentId;
}
});
}
Note: This is a generic insert method that takes the namespaced collection name as a parameter and a document. It is intended to be called from the client side with a callback function which returns either the result as a document id or an error object.

Resources