I've been trying to send an email after a autoform has been successfully submitted. I've tried using the template.events 'submit' which didn't work and I've tried to use metermethod="sendEmail". Nothing I do seems to work. Can someone please tell me what I'm doing wrong.
Path: form.html
{{#autoForm collection="JobOffers" id="jobOfferForm" type="insert" meteormethod="sendEmail"}}
<fieldset>
{{> afQuickField name='firstName'}}
<button type="submit" data-meteor-method="sendEmail" class="btn btn-primary">Submit</button>
</fieldset>
{{/autoForm}}
Path: server/email.js
sendEmail: function (from, subject, userId) {
check([from, subject, userId], [String]);
// Let other method calls from the same client start running,
// without waiting for the email sending to complete.
this.unblock();
SSR.compileTemplate( 'htmlEmail', Assets.getText( 'html-email.html' ) );
// to find the users info for the logged in users
// var user = Meteor.user();
var user = Meteor.users.findOne({ _id: userId });
var email = (user && user.emails[0].address);
var emailData = {
// name: (candidate && candidate.profile && candidate.profile.firstName),
name: (user && user.profile && user.profile.firstName),
// favoriteRestaurant: "Honker Burger",
// bestFriend: "Skeeter Valentine"
};
Email.send({
to: email,
from: from,
subject: subject,
html: SSR.render( 'htmlEmail', emailData )
});
console.log('sendEmail sent');
}
});
UPDATE
Path: form.js
AutoForm.hooks({
jobOfferForm: hooksObject
});
var hooksObject = {
after: {
insert: function(error, result){
Email.send({
var otheruserId = FlowRouter.getParam('id');
Meteor.call('sendEmail',
'test#email.com',
'Hello from Meteor!',
otheruserId);
};
}
}
};
You can use callbacks/hooks of autoform. If you want to send email after an insert following would be a solution:
var hooksObject ={
after: {
insert: function(error, result){
//Send email here
}
}
}
UPDATE:
var hooksObject = {
after: {
insert: function(error, result){
var otheruserId = FlowRouter.getParam('id');
Meteor.call('sendEmail',
'test#email.com',
'Hello from Meteor!',
otheruserId);
}
}
};
AutoForm.addHooks('jobOfferForm', hooksObject);
Please refer to autoform documentation for more info.
Related
I wrote a Reset Password function :
the user collection (services>password>reset) is well setted
the mail is sended well
the link in the mail route me to the right url (right token in it)
on this page, the session resetPasswordToken is set but with
undefined value: I can't automatically write the token here - if I write manually the token here, everything's fine, the password is well reseted, the session resetPasswordToken is after that well setted to null.
So how to write the token in the session ? Thanks.
Below my code.
**
The link client to get a new password
<p class="info">Resend verification link</p>
The event on click (client)
Template.UserProfile.events({
//resend verification link function
'click .send-reset-password-link' ( event, template ) {
Meteor.call( 'sendResetPasswordLink', ( error, response ) => {
if ( error ) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
let email = Meteor.user().emails[ 0 ].address;
Bert.alert({
title: 'Reset Password Link sended',
message: 'Please check your mails.',
type: 'info'
});
}
});
}
});
The method to send the link (server)
Meteor.methods({
sendResetPasswordLink() {
let userId = Meteor.userId();
if ( userId ) {
return Accounts.sendResetPasswordEmail( userId );
}
}
});
The resetpassword email template (server)
//resetPassword Template
Accounts.emailTemplates.siteName = "Me";
Accounts.emailTemplates.from = "Me <my#mail.com>";
Accounts.emailTemplates.resetPassword.subject = function (user) {
return "Reset Your Password";
};
// html template
Accounts.emailTemplates.resetPassword.html = function (user, url) {
SSR.compileTemplate( 'htmlEmail', Assets.getText( 'emailverification.html' ) );
let emailAddress = user.emails[0].address,
userAvatar = user.profile.avatar,
urlWithoutHash = url.replace( '#/', '' );
var emailData = {
urlWithoutHash: `${urlWithoutHash}`,
userAvatar: `${userAvatar}`,
};
return SSR.render( 'htmlEmail', emailData );
};
The route
FlowRouter.route( '/reset-password/:token', {
name: 'reset-password',
action( params ) {
BlazeLayout.render('ResetPassword', {content: 'body'});
Accounts.resetPassword( params.token, ( error ) =>{
if ( error ) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
FlowRouter.go( '/' );
}
});
}
});
And finally, the resetpassword template
//resetpassword.html
<template name="ResetPassword">
<form action="/reset-password" class="reset-password" id="resetPasswordForm" method="post">
<input id="resetPasswordPassword" name="password" placeholder="New Password" type="password" >
<!-- <input id="resetPasswordPasswordConfirm" name="password-confirm" placeholder="Confirm" type="password" > -->
<button class="btn-submit" type="submit" value="Reset">Reset</button>
</form>
<!-- end #reset-password-form -->
</template>
//resetpassword.js
if (Accounts.resetPassword) {
Session.set('resetPasswordToken', Accounts.resetPassword);
}
Template.ResetPassword.helpers({
resetPassword: function(){
return Session.get('resetPasswordToken');
}
});
Template.ResetPassword.events({
"submit .reset-password": (event) => {
// Prevent default browser form submit
event.preventDefault();
//let token;
// Get value from form element
const target = event.target;
const password = event.target.password.value;
// If the password is valid, we can reset it.
if (password) {
//Accounts.resetPassword(token, password, (error) => {
Accounts.resetPassword(Session.get('resetPasswordToken'), password, (error) => {
if (error) {
Bert.alert({
title: 'Error',
message: error.reason,
type: 'danger'
});
} else {
Bert.alert({
title: 'Success',
message: 'Account successfully created.',
type: 'success'
});
Session.set('resetPasswordToken', null);
//Router.go('postsList');
}
});
} else {
Bert.alert({
title: 'Error',
message: 'The password cannot be empty.',
type: 'danger'
});
}
}
I want to save some data and show it in a view template. So I want to do like the example below but using methods.
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val()
};
post._id = Posts.insert(post);
Router.go('postPage', post);
}
});
I tried this:
'insertClubData': function(clubname, capacity, description, homepage){
var currentUserId = Meteor.userId();
var club = {
clubname: clubname,
description: description,
capacity: parseInt(capacity),
homepage: homepage,
createdAt: new Date(),
visitors: 0,
occupancy: 0,
trend: "club-1",
createdBy: currentUserId
}
club._id = clubs.insert(club);
Router.go('club', club);
},
but I get the error:
Exception while invoking method 'insertClubData' TypeError: Object
function router(req, res, next) { I20160425-14:04:55.724(2)? //XXX
this assumes no other routers on the parent stack which we should
probably fix
I understand that this is because Router.go is a client side method. But I also understand that you should avoid server side routing. So what's the most elegant solution?
This is my route:
Router.route('/club/:_id', {
name: 'club',
template: 'club',
data: function(){
return clubs.findOne({_id: this.params._id})
}
});
How about, you call the method from the client and in the callback on success you do the routing. For example:
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val()
};
Meteor.call('insertPost', post, function(error, id) {
if (error) {
alert(error)
} else {
Router.go('postPage', {_id: id});
}
});
}
});
and on the server
Meteor.methods({
insertPost: function(post) {
// do checks
id = Posts.insert(post);
return id;
}
});
Does that work for you?
I have a Meteor project where there's generally a delay during the sign up process for new users due to slow connection etc. Is there a way to create a loading screen during this procedure?
I do currently have the sacha:spin package which I use with the iron:router package when different pages are retrieving data to yield. Eg. for my home page I display all the users (mainly for debugging purposes) using this code in my routes:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading'
});
Router.route('/', {
name: 'home',
controller: 'HomeController'
});
HomeController = RouteController.extend({
template: 'home',
waitOn: function() {
return Meteor.subscribe('users');
}
});
Here's my spinner template (nice and simple):
<template name="loading">
{{> spinner}}
</template>
This setup works so nicely, so I was wondering if it would be easy to implement on my create account procedure that I have here:
Template.signup.events({
'submit #signup-form': function(event, template) {
event.preventDefault();
var validated = true,
username = ...,
email = ...,
firstName = ...,
lastName = ...,
password = ...,
// Validation
if (validated) {
Accounts.createUser({
username: username,
email: email,
password: password,
profile: {
firstName: firstName,
lastName: lastName
}
},
function(err) {
if (err) {
// Handle it
} else {
Router.go('home');
}
});
return false;
}
});
Can I use my loader that I used for my home template with the create account procedure?
I would use reactive-var to handle this.
https://atmospherejs.com/meteor/reactive-var
Then, in your template code:
Template.signup.onCreated( function() {
Template.instance().isLoading = new ReactiveVar(false);
});
Template.signup.helpers({
isLoading() {
return Template.instance().isLoading.get();
}
});
Template.signup.events({
'submit #signup-form': function(event, template) {
event.preventDefault();
var validated = true,
username = ...,
email = ...,
firstName = ...,
lastName = ...,
password = ...,
// Validation
if (validated) {
template.isLoading.set(true);
Accounts.createUser({
username: username,
email: email,
password: password,
profile: {
firstName: firstName,
lastName: lastName
}
},
function(err) {
if (err) {
// Handle it
} else {
Router.go('home');
}
template.isLoading.set(false);
});
return false;
}
});
And in the current signup template you'll need to have the structure:
{{#unless isLoading}}
<!-- YOUR CONTENT GOES HERE -->
{{else}}
{{> loading}}
{{/unless}}
Im working on a project and my last task is to implement publish and subscribe to prevent users seeing conversations they were not involved in. We were given an example code and I notice a filter on the router.
Router.route('/chat/:_id', function () {
// the user they want to chat to has id equal to
// the id sent in after /chat/...
var otherUserId = this.params._id;
// find a chat that has two users that match current user id
// and the requested user id
var filter = {$or:[
{user1Id:Meteor.userId(), user2Id:otherUserId},
{user2Id:Meteor.userId(), user1Id:otherUserId}
]};
var chat = Chats.findOne(filter);
if (!chat){// no chat matching the filter - need to insert a new one
chatId = Chats.insert({user1Id:Meteor.userId(), user2Id:otherUserId});
}
else {// there is a chat going already - use that.
chatId = chat._id;
}
if (chatId){// looking good, save the id to the session
Session.set("chatId",chatId);
}
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
I thought the filter should be on publish and then subscribe. Here is what I have now.
My HTML
<template name="chat_page">
<h2>Type in the box below to send a message!</h2>
<div class="row">
<div class="col-md-12">
<div class="well well-lg">
{{#each recentMessages}}
{{> message}}
{{/each}}
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
{{#if currentUser}}
<form class="new-message">
<input class="input" type="text" name="text" placeholder="type a message here...">
<button class="btn btn-default">Send</button>
</form>
{{/if}}
</div>
</div>
</template>
<!-- simple template that displays a message -->
<template name="message">
<div class = "container">
<div class = "row">
<div class = "username">
<img src="/{{avatar}}" class="avatar_img" >
{{username}}
said: {{messageText}}
</div>
</div>
</div>
</template>
Server Side
Meteor.startup(function () {
if (!Meteor.users.findOne()){
for (var i=1;i<9;i++){
var email = "user"+i+"#test.com";
var username = "user"+i;
var avatar = "ava"+i+".png"
console.log("creating a user with password 'test123' and username/ email: "
+ email);
Meteor.users.insert({
profile:{username:username, avatar:avatar},
emails: [{address:email}],
services:{
password:{"bcrypt" : "PASSWORD REMOVED"}}});
}
}
});
Meteor.publish("messages", function (userId) {
return Messages.find();
});
Meteor.publish("userStatus", function() {
return Meteor.users.find({ "status.online": true });
});
Meteor.publish("userData", function(){
if(this.userId) {
return Meteor.users.find({_id: this.userId},{
fields: {'other':1, 'things': 1}});
} else {
this.ready();
}
return Meteor.users.find({ "status.online": true })
});
Client Side
Meteor.subscribe("messages");
Meteor.subscribe("userStatus");
Meteor.subscribe("userData");
// set up the main template the the router will use to build pages
Router.configure({
layoutTemplate: 'ApplicationLayout'
});
// specify the top level route, the page users see when they arrive at the site
Router.route('/', function () {
console.log("rendering root /");
this.render("navbar", {to:"header"});
this.render("lobby_page", {to:"main"});
});
Router.route('/chat/:_id', function () {
this.render("navbar", {to:"header"});
this.render("chat_page", {to:"main"});
});
///
// helper functions
///
Template.message.helpers({
userName: function() {
var userId = this.userId;
var user = Meteor.users.findOne(userId);
var username = user && user.profile && user.profile.username;
var avatar = user && user.profile && user.profile.avatar;
return {
username: username,
avatar: avatar
}
}
})
Template.available_user_list.helpers({
users:function(){
return Meteor.users.find();
}
})
Template.available_user.helpers({
getUsername:function(userId){
user = Meteor.users.findOne({_id:userId});
return user.profile.username;
},
isMyUser:function(userId){
if (userId == Meteor.userId()){
return true;
}
else {
return false;
}
}
})
Template.chat_page.helpers({
recentMessages: function () {
if (Session.get("hideCompleted")) {
return Messages.find({checked: {$ne: true}}, {sort: {createdAt: -1}});
} else {
return Messages.find({}, {sort: {createdAt: 1}});
}
},
hideCompleted: function () {
return Session.get("hideCompleted");
},
incompleteCount: function () {
return Tasks.find({checked: {$ne: true}}).count();
}
});
Template.chat_page.events({
// this event fires when the user sends a message on the chat page
'submit .new-message':function(event){
console.log(event);
event.preventDefault();
var text = event.target.text.value;
// stop the form from triggering a page reload
event.target.text.value = "";
// see if we can find a chat object in the database
// to which we'll add the message
Meteor.call("sendMessage", text);
},
});
Collections
Messages = new Mongo.Collection("messages");
Shared-Methods
Meteor.methods({
sendMessage: function (messageText) {
if (! Meteor.userId()) {
throw new Meteor.Error("not-authorized");
}
Messages.insert({
messageText: messageText,
createdAt: new Date(),
username: Meteor.user().profile.username,
avatar: Meteor.user().profile.avatar,
});
}
});
I have the following meteor method
hasNoPendingPayments: function() {
var userId = Meteor.userId();
console.log(userId); <---------------------- correctly logs userId
var user = Users.findOne({_id: userId }, { fields: { services: 0 } });
console.log(user); <-------------------------- logs 'undefined'
return hasNoPendingPayments(user);
},
This private helper I call from the above
hasNoPendingPayments = function(user) {
// console.log('hasNoPendingPayments ');
// console.log(user);
var payments = Payments.find({ userId: user._id, status: {
$in: [Payments.States.PENDING, Payments.States.PROCESSING]}
});
return payments.count() === 0;
};
And I call it from the client here
Template.payments.created = function() {
this.hasNoPendingPayments = new ReactiveVar(false);v
};
Template.payments.rendered = function () {
Session.set('showPaymentRequestForm', false);
var self = this;
Meteor.call('hasNoPendingPayments', function(error, result) {
if (result === true) { self.hasNoPendingPayments.set(true); }
});
...
However, I get an undefined error on the server when I load the template initially (I marked where in code). Although, when I try call the same query on the client with the same userId, i correctly gets the user record
Any idea as to why this is?
Try with this.
Template.payments.rendered = function () {
Session.set('showPaymentRequestForm', false);
var self = this;
if(Meteor.userId()){
Meteor.call('hasNoPendingPayments', function(error, result) {
if (result === true) { self.hasNoPendingPayments.set(true); }
});
}else{
console.log("Seems like user its not logged in at the moment")
}
Maybe when you make the Meteor.call, the data its not ready
Also just to be sure, when you run Users.findOne({_id: userId }, { fields: { services: 0 } }); on console.log what you get?
Maybe the find is wrong or have some typo
update
Router.map(function()
{
this.route('payments',
{
action: function()
{
if (Meteor.userId())
this.render();
} else{
this.render('login') // we send the user to login Template
}
}
}
or waitOn
Router.map(function () {
this.route('payments', {
path: '/payments',
waitOn: function(){
return Meteor.subscribe("userData"); //here we render template until the subscribe its ready
}
});
});
Meteor stores all the user records in Meteor.users collection
so try Meteor.users.findOne({_id: userId }....)
Instead of Users.findOne({_id: userId }, { fields: { services: 0 } });
in your server method