yads / nodemailer-express-handlebars

A plugin for nodemailer that uses express-handlebars view engine to generate emails
84 stars 29 forks source link

Error: ENOENT: no such file or directory for template #43

Closed emarel closed 3 years ago

emarel commented 3 years ago

I am having an issue where nodemailer-express-handlebars is outputting the error:

[Error: ENOENT: no such file or directory, open '/Users/person/Code/app/templates/undefined.hbs'] {
  errno: -2,
  code: 'ENOENT',
  syscall: 'open',
  path: '/Users/person/Code/app/templates/undefined.hbs'

My setup is as follows (using advice/solution from a previous post from @nbrookie)

 const handlebarOptions = {
    viewEngine: {
      extName: ".hbs",
      partialsDir: path.resolve(__dirname, "templates"),
      defaultLayout: false
    },
    viewPath: path.resolve(__dirname, "templates"),
    extName: ".hbs"
  };

  transporter.use('compile', hbs(handlebarOptions));

and sending the email with:

let mailOptions = await transporter.sendMail({
    from: '"Test - No Reply" <test@test.com>'
    to: 'someEmail@gmail.com, 
    subject: "Hello ✔",
    template: 'welcome',
  });

Oddly enough I still receive the email even though it says it cant find the file. How is this possible and how can I resolve this issue?

nachiketkallapur commented 3 years ago

Even I'm getting same error but error number is "-4058"

emarel commented 3 years ago

Even I'm getting same error but error number is "-4058"

Also interesting to note...

I changed 'welcome' to 'welcome.hbs' in the nodemailer and now the error is saying it can't find 'welcome.hbs.hbs'. This makes sense, you would think the solution would be to remove the '.hbs' and make it 'welcome' but then we are back to the original error of 'undefined.hbs'.

Also, if i change the template to 'welcome2' it says it cant find 'welcome2.hbs'. Its weird... it's as though it becomes undefined only when the template file matches the filename which is what it should be.

Hoping this helps identify the issue more

nachiketkallapur commented 3 years ago

It worked for me

const nodemailer = require("nodemailer");
const hbs = require("nodemailer-express-handlebars");

const path = require('path');

var transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: "useremail",        //Add your email
    pass: "password",        //Add your password
  },
});

const handlebarOptions = {
  viewEngine: {
    extName: ".handlebars",
    partialsDir: path.resolve(__dirname, "templateViews"),
    defaultLayout: false,
  },
  viewPath: path.resolve(__dirname, "templateViews"),
  extName: ".handlebars",
};

transporter.use(
  "compile",
  hbs(handlebarOptions)
);

var mailOptions = {
  from: "username",
  to: req.body.email,
  subject: "Testing NodeMailer",
  template: "main",              //Any template stored in viewPath
};

transporter.sendMail(mailOptions, (error, info) => {
  if (error) {
    console.log(error);
  } else {
    res.send({
      status: "success",
      data: "Reset Link sent successfully",
    });
    console.log("Email sent: " + info.response);
  }
});
emarel commented 3 years ago

It worked for me

const nodemailer = require("nodemailer");
const hbs = require("nodemailer-express-handlebars");

const path = require('path');

var transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: "useremail",        //Add your email
    pass: "password",        //Add your password
  },
});

const handlebarOptions = {
  viewEngine: {
    extName: ".handlebars",
    partialsDir: path.resolve(__dirname, "templateViews"),
    defaultLayout: false,
  },
  viewPath: path.resolve(__dirname, "templateViews"),
  extName: ".handlebars",
};

transporter.use(
  "compile",
  hbs(handlebarOptions)
);

var mailOptions = {
  from: "username",
  to: req.body.email,
  subject: "Testing NodeMailer",
  template: "main",              //Any template stored in viewPath
};

transporter.sendMail(mailOptions, (error, info) => {
  if (error) {
    console.log(error);
  } else {
    res.send({
      status: "success",
      data: "Reset Link sent successfully",
    });
    console.log("Email sent: " + info.response);
  }
});

Appreciate you providing the code! I was able to get it to work with your implementation. For those that come across this issue it looks as though making it a async function with a callback caused some type of undefined issue. See code below for how the issue was caused. Thanks again @nachiketkallapur

async function sendMail(user, callback) {
  var transporter = nodemailer.createTransport({
    service: "gmail",
    auth: {
      user: "userEmail",
      pass: "Password",
    },
  });

  const handlebarOptions = {
    viewEngine: {
      extName: ".handlebars",
      partialsDir: path.resolve(__dirname, "templateViews"),
      defaultLayout: false,
    },
    viewPath: path.resolve(__dirname, "templateViews"),
    extName: ".handlebars",
  };

  transporter.use(
    "compile",
    hbs(handlebarOptions)
  );

  let mailOptions = await transporter.sendMail({
    from: "username",
    to: 'req.body.email',
    subject: "Testing NodeMailer",
    template: "main"
  });

  let info = await transporter.sendMail(mailOptions);

  callback(info);
}
nachiketkallapur commented 3 years ago

@emarel Do you whether can we pass any argument to the handlebars file that is rendered in the above code? (I mean passing some dynamic values to handlebars email template from above configuration)

emarel commented 3 years ago

@emarel Do you whether can we pass any argument to the handlebars file that is rendered in the above code? (I mean passing some dynamic values to handlebars email template from above configuration)

Yep, very easy to do. All you need to do is add a context object in the mailOptions object

 var mailOptions = {
   from: "username",
    to: 'req.body.email',
    subject: "Testing NodeMailer",
    template: "main"
    context: {
      to: recipientName,
      from: senderName,
      value: inputValue
    }
  };

In the handlebars file you can access the variable with {{value}}

moongl4de commented 3 years ago

@emarel @nachiketkallapur could you take a look at this and see what I'm doing wrong?

I implemented your code but still getting the -4058 error. Any help would be awesome!

const nodemailer = require("nodemailer");
const hbs = require("nodemailer-express-handlebars");
const express = require("express");
const app = express();
const port = 3000;
var path = require("path");

var transporter = nodemailer.createTransport({
  service: "gmail",
  auth: {
    user: "myemail, //Add your email
    pass: "mypassword", //Add your password
  },
});

const handlebarOptions = {
  viewEngine: {
    extName: ".handlebars",
    partialsDir: path.resolve(__dirname, "views"),
    defaultLayout: false,
  },
  viewPath: path.resolve(__dirname, "views"),
  extName: ".handlebars",
};

transporter.use("compile", hbs(handlebarOptions));

var mailOptions = {
  from: "emailhere",
  to: "emailhere",
  subject: "Testing NodeMailer",
  template: "main", //Any template stored in viewPath
};

transporter.sendMail(mailOptions, (error, info) => {
  if (error) {
    console.log(error);
  } else {
    res.send({
      status: "success",
      data: "Reset Link sent successfully",
    });
    console.log("Email sent: " + info.response);
  }
});

app.listen(port, () => {
  console.log(
    `Server listening at http://localhost:${port}`
  );
});
nachiketkallapur commented 3 years ago

Thanks @emarel for your reply. I had searched more than a week for that before your response

nachiketkallapur commented 3 years ago

@alexgignilliat I tried your code. It's working in my system.

moongl4de commented 3 years ago

@nachiketkallapur thanks for the reply! So, I've got it sending an email now without an error, but it's a blank email. None of my handlebars is being sent. Any ideas would be much appreciated!!

moongl4de commented 3 years ago

@nachiketkallapur thank you for the info, but the issue is really that the data that should be passed into 'main.handlebars' from 'index.handlebars' doesn't show up in the email. I'm trying to dynamically populate the html before sending the email. (I know my code for handlebars itself is fine as I've tested it)

nachiketkallapur commented 3 years ago

Try saving your handlebars code.

Try with handlebars code from https://webdesign.tutsplus.com/articles/creating-a-simple-responsive-html-email--webdesign-12978

Also, note that few styling methods aren't compatible with the html parsers used by various mail services. Ex z-index, background-image etc.

nachiketkallapur commented 3 years ago

You can pass data from a .js file to .handlebars file by using the context property(as shown in the above code)

I have no idea on passing dynamic data from one .handlebars to another .handlebars file

balakumararjunan commented 4 months ago

@nachiketkallapur nodeerr Still I am having error, can't resolve, kindly help