How to display the errors in a meteor view template - meteor

How can I go about getting the value of the errors I have thrown in a fails insert on a collection or in a method.
Customers.allow({
insert: function(userID, rec) {
console.log(userID === rec.userID);
if (rec.userID === null) {
throw new Meteor.Error(600, "You must be logged in");
};
if (rec.phone.length != 10 ) {
throw new Meteor.Error(601, "Incorect phone format", "Phone must be 10 chars long");
};
if (rec.fax.length != 10 ) {
throw new Meteor.Error(602, "Incorect fax format", "Fax must be 10 chars long");
};
return userID === rec.userID;
}
});
So right now i see the error on the console but say if wanted this display the errors in the template or store it in a reactive session so it can be shown to the user to correct.
like try to to something like this.
Template.form.errors = function () {
// return however you get to those thrown errors
}

There was a package just released today to help with this : https://github.com/tmeasday/meteor-errors
You would need meteorite to use it : https://github.com/oortcloud/meteorite
Add the package with meteorite:
mrt add errors
(Don't worry you're not adding errors to your meteor besides the command ;)
You can then throw errors in your client js:
Meteor.Errors.throw("Error details");
Then wherever you want to display errors use, in your HTML:
{{>meteorErrors}}

Related

Is it possible to set another user's password in Meteor if you have admin privileges?

I am now trying to set another user's password in Meteor admin page.
Here is my code.
Meteor.methods({
updateUserPassword: function(userId, password) {
var loggedInUser = Meteor.user()
if (!loggedInUser ||
!(Roles.userIsInRole(loggedInUser, ['admin'], 'default_group')) || (loggedInUser._id == userId) ) {
throw new Meteor.Error(403, "Access denied")
}
return Accounts.setPassword(userId, password);
}
});
But when I run this code, I get Accounts.setPassword is undefined error.
I added accounts-password and accounts-base packages, but it still shows undefined error, so I suspect if the Accounts.setPassword is not supported anymore.
Please help me how to handle this problem!
Accounts.setPassword is a server-only function in Meteor. If you are getting the error in your browser console it is because your updateUserPassword method is declared in lib/ folder or somewhere similar and can be accessed by both client-side and server-side.
Usually, it is desirable for Meteor.methods to be declared in lib/ folder in order to take advantage of Meteor's Latency Compensation technique (also called Method Simulation).
In your case that is not desirable because Accounts.setPassword is server-only.
Solution 1:
You can use Meteor.isClient and Meteor.isServer to determine which code to run where. (You can also use this.isSimulation).
Meteor.methods({
updateUserPassword: function(userId, password) {
var loggedInUser = Meteor.user()
if (!loggedInUser ||
!(Roles.userIsInRole(loggedInUser, ['admin'], 'default_group')) || (loggedInUser._id == userId) ) {
throw new Meteor.Error(403, "Access denied")
}
if(Meteor.isServer) {
return Accounts.setPassword(userId, password);
} else if(Meteor.isClient) {
// do something else
}
}
});
Solution 2:
You can declare the Meteor.methods on the server-side by placing the file in the server-only server/ folder, or placing the whole of the Meteor.methods declaration within a if(Meteor.isServer) { ... } check.
This should be used when latency compensation is not needed.

ironRouter onbeforeaction gives 2 errors, but does what I expect it to do

Users login using FB or twitter:
I'm trying to check for multiple things here as you can see. But for some reason I get 2 errors:
1. Exception in callback of async function: TypeError: Cannot read property 'profile' of undefined
2. Route dispatch never rendered. Did you forget to call this.next() in an onBeforeAction?
The funny thing is, this code IS doing what I expected it to do. Route to completeSignup if profile.firsttime = false and if not logged in go to startPage. But I still get these errors, so I must be doing something wrong.
code:
onBeforeActions = {
loginRequired: function() {
if (!Meteor.userId()) {
Router.go('startPage');
} else {
if (Meteor.userId() && Meteor.user().profile.firsttime) {
Router.go('completeSignup');
}
}
this.next();
}
};
Router.onBeforeAction(onBeforeActions.loginRequired, {
except: ['startPage']
});
Meteor.userId() becomes available as part of the login process prior to the arrivial of the user document on the client. Mixing the two in the if actually doesn't do what you want because, for a brief moment, they won't simultaneously return truthy values.
In order to avoid the error you'll need to add some extra guards. Try something like this in your else clause:
var user = Meteor.user();
if (user && user.profile && user.profile.firsttime) {
Router.go('completeSignup');
}

Meteor template updates before result of Meteor.users.update

I'm trying to figure out how to prevent a template from updating until Meteor.users.update() finishes.
First I'm trying to make sense of the documentation and the use of an optional callback argument in order to sort out what is happening.
Here is what I have:
Meteor.users.update(Meteor.userId(),
{$set:{'profile.reviewList': []}},
[],
function(err, result){
if (err){
console.log('oh no!');
} else {
console.log('Result achieved: '+this.profile.reviewList);
}
});
Currently the console.log('Result achieved: '+this.profile.reviewList); always returns something like ...TypeError: Cannot read property 'reviewList' of undefined... the first time though which tells me its firing before the result comes back.
I'm sure I'm not implementing the callback properly, but I was attempting to model this answer: How do you ensure an update has finished in meteor before running a find?
I'd really just like to delay the re-rendering of the associated template until the property gets created.
Any help will be greatly appreciated.
You assume that scope (this) in callback function return user object, which is wrong.
If you want to get user object in that callback simply query it there:
var user = Meteor.users.find(Meteor.userId()).fetch()
Another thing, you passed empty array as 2nd argument which is not needed.
Meteor.users.update(
Meteor.userId(),
{
$set: {
'profile.reviewList': 'testData'
}
},
function(err, result) {
if (err) {
console.log('oh no!');
} else {
var user = Meteor.users.find(Meteor.userId()).fetch();
console.log('Result achieved: ' , user && user.profile && user.profile.reviewList);
}
}
);

Add extra user field

In my Meteor app I use the default accounts package, which gives me the default login and registration functionality. Now I want to add an extra field to user, say nickname, and for the logged in user the possibility to edit this information.
For editing the profile I suppose I should be doing something like this:
Template.profileEdit.events({
'submit form': function(e) {
e.preventDefault();
if(!Meteor.user())
throw new Meteor.Error(401, "You need to login first");
var currentUserId = this._id;
var user = {
"profile.nickname": $(e.target).find('[name=nickname]').val()
};
Meteor.users.update(currentUserId, {
$set: user
}, function(error){
if(error){
alert(error.reason);
} else {
Router.go('myProfile', {_id: currentUserId});
}
});
}
});
But I doesn't store the info if I look in Mongo. Also when showing the profile, {{profile.nickname}} returns empty. What is wrong here?
Edit: added collections\users.js to show permissions:
Meteor.users.allow({
update: function (userId, doc) {
if (userId && doc._id === userId) {
return true;
}
}
});
Meteor.users.deny({
update: function(userId, user, fieldNames) {
return (_.without(fieldNames, 'profile.nickname').length > 0);
}
});
Yeah, I believe that should do the job, although I haven't actually run the code. The idea is certainly right.
The main things to be aware of are:
The necessity to allow the user doc to be edited from the client with an appropriate Meteor.users.allow() block on the server, assuming you're going to remove the "insecure" package (which you need to before doing anything in production).
The fact that "by default the server publishes username, emails, and profile", so you'll need to write a Meteor.publish function on the server and subscribe to it if you want to expose any other fields within the user document to the client once you've removed the "autopublish" package (which again, you really should).

Extending the Meteor loginWithPassword method

I may be totally off line here, but I'm trying to extend the loginWithPassword method in Meteor to handle only returning users with a few parameters set in their profile.
I'm creating the users fine and once created they login as that user type and all is good, but, when I try and login again I hit a wall.
I've tried implementing my own login handler as follows...
Accounts.registerLoginHandler(function(loginRequest) {
console.log("Got to Accounts.registerLoginHandler");
console.log(loginRequest);
var userId = null;
var user = Meteor.loginWithPassword(loginRequest.email, loginRequest.password, function(error){
if(error !== undefined){
setAlert('error', 'Error in processing login. ' + error.reason + '.');
}
});
var userWithType;
if(user){ // we have the right username and password
console.log("Found a user and logged them in");
userWithType = Meteor.users.findOne({'id': user._id, 'profile.type': loginRequest.type});
}
if(userWithType){
console.log("Found User of that type")
userId = user._id;
}
console.log("UserId", userId);
return {
id: userId
}
});
But am getting an error when I get to this code that says
Got to Accounts.registerLoginHandler
{ email: 'blah2#blah', password: 'blha', type: 'user' }
Exception while invoking method 'login' TypeError: Object #<Object> has no method 'loginWithPassword'
at app/server/login.js:8:23
at tryAllLoginHandlers (app/packages/accounts-base/accounts_server.js:53:18)
at Meteor.methods.login (app/packages/accounts-base/accounts_server.js:73:18)
at maybeAuditArgumentChecks (app/packages/livedata/livedata_server.js:1367:12)
at _.extend.protocol_handlers.method.exception (app/packages/livedata/livedata_server.js:596:20)
at _.extend.withValue (app/packages/meteor/dynamics_nodejs.js:31:17)
at app/packages/livedata/livedata_server.js:595:44
at _.extend.withValue (app/packages/meteor/dynamics_nodejs.js:31:17)
at _.extend.protocol_handlers.method (app/packages/livedata/livedata_server.js:594:48)
at _.extend.processMessage.processNext (app/packages/livedata/livedata_server.js:488:43)
I'm obviously missing a this pointer or something like that, but don't know enough about this framework to know if I'm totally off track here even trying to get this to work.
Ta
P.
I am not too familiar with it but from http://docs.meteor.com, Meteor.loginWithPassword () can only be called on the client. You have written it into the server side code from the tutorial.
That is throwing the error you see. If you move it to the client you will also see that it only returns to the callback function so your variable user will remain undefined.
Meteor.user().profile is available on the client so you can just check the type there in the callback of loginWithPassword to check the information at login.

Resources