Carrying data with router.go() in Iron Router - meteor

I'm struggling to pass data from one template to another, and thinking about it I wonder if that's my problem anyway. I'm using the built in accounts system, I have adding new users and authentication working, and when a user signs in I forward them to a new template. I'd like to be able to use their details in that template but I'm struggling to figure out the best way to do this.
Initially I thought I could simply use: Router.go('userPage', {user:username}); which gives no errors but doesn't work. In my template I'm using : <p>Welcome {{user}}</p>
Using {{> user}} throws an 'Can't find template, helper or data context key: username' error.
Any ideas?
EDIT: Ignore the rest, after restarting the Meteor server this is now working.
Thinking I have a bigger issue here so adding more detail:
Taking some info from a form and then routing based upon the outcome:
Meteor.loginWithPassword(username, password, function(err) {
if (err) {
console.log('Logging in failed');
} else {
console.log('Logging in succeeded');
console.log(username);
Router.go('userPage');
}
});
I then wish to open this template and pass through the data of the user who just logged in:
<template name="userPage">
<div class="container">
<h1>Welcome {{username}}</h1>
</div>
<p>User Page</p>
</template>
If I use <p>Welcome {{currentUser.username}}</p> I get no errors but also no name. Meteor.user().username does return a name but I cannot get that into the template.

"username" is undefined, you should use Meteor.user().username
Or better yet, you should use the currentUser predefined helper which contains the currently logged in user that you can pass between pages.
<p>Welcome {{currentUser.username}}</p>
The usual way to pass data between templates is by transmitting the document _id in the route url then retrieving it with the Iron Router and doing your stuff with the doc.
Sometimes it is easier to store currentSomething in a global helper, with a reactive Session variable keeping track of the "something _id" but it can lead to pretty sloppy code, beware !

Related

Setting up 'Trigger Email' Firebase Extension

I learned about firebase and cloud functions recently and have been able to develop simple applications with them.
I now want to expand my knowledge and really struggling with Trigger Email Extension.
On a specific event on my firebase, I want to fire an email to the user in a custom format, but I am unable to even activate the extension for now.
Can someone please explain with example please about these fields marked in the picture?
I had this question too, but got it resolved. Here's your answer:
"Email documents collection" is the collection that will be read to trigger the emails. I recommend leaving named "mail" unless you already have a collection named mail.
"Users collection (Optional)" refers to a collection (if any) that you want to use in tandem with a user auth system. I haven't had this specific use case yet, but I imagine once you understand how Trigger Email operates, it should be somewhat self-explanatory.
"Templates collection (Optional)" is helpful for templates in which you can use handlebar.js is automatically input specific information per user. (eg. <p>Hello, {{first_name}}</p> etc.) Similar to the previously mentioned collections, you can name it whatever you want.
How to create a template (I have yet to actually implement this, so take this with a grain of salt):
In your templates collection, you want to name each document with a memorable ID. Firebase gives the example:
{
subject: "#{{username}} is now following you!",
html: "Just writing to let you know that <code>#{{username}}</code> ({{name}}) is now following you.",
attachments: [
{
filename: "{{username}}.jpg",
path: "{{imagePath}}"
}
]
}
...specifying a good ID would be following. As you can see, the documents should be structured just like any other email you would send out.
Here is an example of using the above template in javascript:
firestore()
.collection("mail")
.add({
toUids: ["abc123"], // This relates to the Users Collection
template: {
name: "following", // Specify the template
// Specify the information for the Handlebars
// which can also be pulled from your users (toUids)
// if you have the data stored in a user collection.
// Of course that gets more into the world of a user auth system.
data: {
username: "ada",
name: "Ada Lovelace",
imagePath: "https://path-to-file/image-name.jpg"
},
},
})
I hope this helps. Let me know if you have an issues getting this set up.

Secure way to store pending user's password in Meteor

For my Meteor application, I would like to have the following signup process:
User registers username, email and password. (He's not able to log in yet.)
Confirmation email sent [Accounts.sendEnrollmentEmail]
User confirms email [Accounts.onEnrollmentLink]
User is created. [Accounts.createUser] (He's able to log in.)
In order to achieve this, I feel like I would have to store the plain text password in a table of temporary users (step 1) in order to create the actual user later (step 3). Obviously this is a horrible idea.
I could of course only ask for the password as of step 3 and create the user at once - but it's not the behavior I would like to achieve.
So: Is there a proper way to store the password securely to later pass it to the user creation? Or is there a way to create a not-loginable users?
There is not much you have to do yourself as Meteor brings everything you need for save password storage when you create a user with the built in methods. So you should use these methods from the beginning (Your step 1: Accounts.createUser, step 2: Accounts.sendVerificationEmail, step 3: Accounts.verifyEmail, step 4 isn't necessary anymore).
Now to get where you want to be you can use an approach like David Weldon suggested but use Accounts.validateLoginAttempt on the sever side. That is a little easier and the login isn't allowed in the first place.
For example you could have this code server side:
Accounts.validateLoginAttempt(function(loginAttempt){
if (!loginAttempt.allowed) {
// Only tell the user that something went wrong but not what to enhance security
throw new Meteor.Error(901, 'Your login credentials are wrong. Try again.');
} else {
// In some cases this method isn't invoked with a correct user object...
if (!loginAttempt.user) {
throw new Meteor.Error(902, 'No valid user object. Make sure to validate your email address first.');
}
// If email verification is required check if the user has a valid email address and don't allow the login if he has none
if (!loginAttempt.user.emails[0].verified) {
throw new Meteor.Error(902, 'Your email address has to be verified first.');
}
// We have a correct login!
return true;
}
});
And now on the client side you can use a logic like this for the login
Meteor.loginWithPassword(email, password, function(callback) {
if (callback === undefined) {
// Your login logic
} else if (callback.error == 902) {
// Your "not verfied" logic
} else {
// Your other login errors logic
}
}
Note that you may have to adjust the registration process a little bit, too, as Meteor per default tries to login users directly after registration but this will not be possible anymore.
Also note that you may use Accounts.validateLoginAttempt for more than just that. For example you also could implement a logic here to only allow a certain amount of bad login attempts from the same IP.
We've used a slightly different pattern in our app based on the accounts package
User registers
User is logged in normally
Out main site template conditions content with
<template name="main">
{{#if currentUser}}
{{#if verified }}
...
{{else}}
Notice to user to look for their verification email
{{/if}}
{{/if}}
</template>
Based on a helper function
Template.main.helpers({
verified: function() { return Meteor.user().emails[0].verified; }
});
This meets the requirement that the user can't do much if anything until they have been verified yet uses the accounts package in a simple and secure way.
I could see taking a related approach using iron:router as well.

Previous page location on IronRouter

Is there a way to get the previous page location before going to the next page in IronRouter?
Is there an event I can use to fetch this information?
Thanks in advance.
Since Iron Router uses the usual History API, you can just use the plain JS method:
history.go(-1);
or
history.back();
Edit: or to check the previous path without following it:
document.referrer;
You can achieve the behavior you want by using hooks.
// onStop hook is executed whenever we LEAVE a route
Router.onStop(function(){
// register the previous route location in a session variable
Session.set("previousLocationPath",this.location.path);
});
// onBeforeAction is executed before actually going to a new route
Router.onBeforeAction(function(){
// fetch the previous route
var previousLocationPath=Session.get("previousLocationPath");
// if we're coming from the home route, redirect to contact
// this is silly, just an example
if(previousLocationPath=="/"){
this.redirect("contact");
}
// else continue to the regular route we were heading to
this.next();
});
EDIT : this is using iron:router#1.0.0-pre1
Apologies for bumping an old thread but good to keep these things up to date saimeunt's answer above is now deprecated as this.location.path no longer exists in Iron Router so should resemble something like the below:
Router.onStop(function(){
Session.set("previousLocationPath",this.originalUrl || this.url);
});
Or if you have session JSON installed (see Session JSON)
Router.onStop(function(){
Session.setJSON("previousLocationPath",{originalUrl:this.originalUrl, params:{hash:this.params.hash, query:this.params.query}});
});
Only caveats with thisis that first page will always populate url fields (this.url and this.originalUrl there seems to be no difference between them) with full url (http://...) whilst every subsequent page only logs the relative domain i.e. /home without the root url unsure if this is intended behaviour or not from IR but it is currently a helpful way of determining if this was a first page load or not

Meteor.user() not fully loaded after login

I am using Meteor 0.8.2 with accounts-facebook. I set up a limited publication for the users this way:
Meteor.publish('users', function () {
return Meteor.users.find({}, {fields: {'profile.picture': 1, 'profile.gender':1, 'profile.type':1}, sort: {'profile.likes': -1}});
});
Now this works great: when I requests a user list from the client I get a list of all users, with the current user's fields all shown and only the 3 published fields for the others. Except: right after login.
When I login and type Meteor.user(), here is what I get:
_id: "uACx6sTiHSc4j4khk"
profile: Object { gender="male", type="1", picture="http://....jpg"}
This stays like that until I refresh the page using the browser button. After refreshing, Meteor.user() gives all the fields available, while Meteor.users.find() still gives the correct restrictions. (except for the current user of course)
Why does my current user not get all its fields right away? I read about a Meteor.userLoaded() method used to wait for the user to be loaded, but it seems to be obsolete in the latest version.
You're running into an interaction between the restriction of merging fields across publications, and the default user publication which sends the profile field.
First, note that there is a built-in publication that always sends the currently logged in user's entire profile field to that user:
https://github.com/meteor/meteor/blob/devel/packages/accounts-base/accounts_server.js#L1172
Second, merging of fields at more than one level deep is currently not supported:
https://github.com/meteor/meteor/issues/998
What you currently have is an issue where the default publication is sending something like the following
{
username: ...,
emails: [ ... ],
profile: {
... all fields ...
}
}
whereas the publication you have set up is sending
{
profile: {
picture: ...
gender: ...
type: ...
}
}
These get merged on the client according to the rules for how subscriptions are resolved (http://docs.meteor.com/#meteor_subscribe). In particular, see the last paragraph. Meteor knows to merge the username and email fields with the profile field. However, it doesn't do this merging at the inner level. So one of the profile fields will get chosen arbitrarily to show up in the client's collection. If the first one wins, you will see profile.likes. If the second one wins, you won't.
It's likely that this behavior is somewhat deterministic and changes depending on whether a normal login handler is called or a resume handler (i.e. when reloading the browser). Hence why it looks like it hasn't loaded.
As Andrew explained, and as I kinda thought, what happened is that there is another "hidden" publication for the current user, which conflicts with mine. All I had to do in order to fix this was to simply exclude the current user from my publication, since it is already fully published by default:
Meteor.publish('users', function () {
return Meteor.users.find({_id:{$ne: this.userId}}, {fields: {'profile.picture': 1, 'profile.gender':1, 'profile.type':1}, sort: {'profile.likes': -1}});
});
This simple $ne does it for me.

Accounts.OnCreateUser() not firing

I'm following this fantastic tutorial on customizing login found as the answer on this post - http://goo.gl/VLO34 - but it's not working for me.
I copied all the files verbatim, and even added supplementary smart packages such as underscore _.pick, and http Meteor.http.get just in case it was required, and mistakenly left out.
Anyhoo - I get github to authorize my app, but Meteor.users in the web console and db.users.findOne() on the local mongo instance show that Accounts.OnCreateUser() didn't add any new information to my user profile [that I'm pulling in from github]. In other words, {{currentUser.profile.avatar_url}} and {{currentUser.profile.login}} won't reveal anything following that tutorial. So I get blank info on the screen.
I tried that screencasts first attempt, and noticed that {loginButtons}} returns values for {{currentUser.profile.login}}. I've reviewed the code many times for typos, but feel that something is quite off with Accounts.onCreateUser(fn)...
I'm using Meteor 0.5.7, and if anyone else has experienced this problem following that screencast, please let me know. Thanks,
EDIT: I've deployed the project to - http://rptest-customlogin.meteor.com/.
Author of the screencast here. And as of a few seconds ago, a new user on your site :-). So, it looks like login is working on your site. But I'm guessing what's happening in your app is the login info isn't available yet at the time of rendering or at the time you're printing to the console. The reason is that all of that info is being populated asynchronously. It takes a few seconds for the process to complete. If you're relying on Meteor.user().profile data in your templates you need to check first if the login process is still underway.
To do that, you can use either the Meteor.loggingIn() javascript function or the {{#if loggingIn}} handlebars block helper. That function is "reactive" which means once the result changes from true to false your UI will update. So the template might look something like this:
<template name="loginDependentWidget">
{{#if loggingIn}}
Logging In
{{else}}
{{currentUser.profile.avatar_url}}
{{/if}}
</template>
Does this help?
Might be a typo but Accounts.OnCreateUser(fn); should be Accounts.onCreateUser(fn);
Meteor docs: http://docs.meteor.com/#accounts_oncreateuser
And then another post on the same subject:
Meteor login with external service: how to get profile information?
EDIT:
Posting as edit due the formatting of the below piece of code.
In the meantime I have got it running on my own project with this piece of code:
Accounts.onCreateUser(function(options, user) {
if(!options || !user) {
console.log('error creating user');
return;
} else {
if(options.profile) {
user.profile = options.profile;
}
}
return user;
});
Which is working just fine. Have you placed the Accounts.onCreateUser(); on the server?

Resources