So I am creating a quote request contact form on a website and I need to make a confirmation or error message pop up under the form after it has been submitted. The issue I am faced with is how can I set a variable on the express side based on whether there was an error or not with the email sending and then use that variable within my handlebars template in order to display the proper message. I'm thinking I would use a helper to achieve this but I keep hitting a wall on attempting it. The logic should begin withing the transporter.sendMail as that is where the error will be determined. I put comments in to help identify. Here is the backend of my contact form:
// ==== Contact Form ====
//Create Mailer options
const options = {
viewEngine: {
extname: '.hbs',
layoutsDir: __dirname + '/views/email/',
defaultLayout: 'template',
partialsDir: 'views/partials/'
},
viewPath: 'views/email/',
extName: '.hbs'
};
// Create Transporter
const transporter = nodemailer.createTransport({
host: 'smtp-mail.outlook.com',
port: 587,
auth: {
user: process.env.USER,
pass: process.env.PASS
}
});
// verify connection configuration
transporter.verify(function(error, success) {
if (error) {
console.log('Error with transporter verification:' + `\n${error}`);
}
});
//attach the plugin to the nodemailer transporter
transporter.use('compile', hbs(options));
app.post('/send', (req, res) => {
// Accepts the form data submitted and parse it
let form = new multiparty.Form();
let data = {};
form.parse(req, function(err, fields) {
Object.keys(fields).forEach(function(property) {
data[property] = fields[property].toString();
});
// Create Mail object with options
const mail = {
from: `"********" <${process.env.USER}>`,
to: '************', // receiver email,
subject: 'Quote Request',
template: 'email.body',
// Import variables into email for use with handlebars
context: {
name: data.name,
email: data.email,
number: data.number,
message: data.message
}
};
// Send email
transporter.sendMail(mail, (err, data) => {
if (err) {
console.log(err);
// if error return mailError = true;
}
else {
console.log('Email successfully sent to recipient!');
// if sent return mailSent = true;
}
});
});
});
Here is my script.js:
// Contact Form Client Functions
//get the form by its id
const form = document.getElementById('contact-form');
//add event listener (when clicking the submit button, do the following)
const formEvent = form.addEventListener('submit', (event) => {
// Prevent page from refreshing when submit button clicked
event.preventDefault();
//parse data to formData variable
let mail = new FormData(form);
//send mail
sendMail(mail);
// Determine if sendMail returned an error or not
console.log(typeof mailError);
// reset form feilds to empty
form.reset();
});
const sendMail = (mail) => {
console.log('step 1');
fetch('/send', {
method: 'post',
body: mail
}).then((response) => {
return response.json();
});
};
and here is the section within my template.hbs file that I need dynamically updated:
<div>
{{#if mailSent}}
<h4 style="color: lightgreen">Your message has been sent successfully!</h4>
{{else if mailError}}
<h4 style="color: red">ERROR: There was an issue sending your message, please
try again.</h4>
{{/if}}
</div>
I think you are mixing Server Side Rendering vs Client Side Rendering strategies (I suggest you to read this to understand the difference). Typically you'd want to use one or the other.
Server Side Rendering Approach: Here is a quick StackBlitz example I did based on your code using server side rendering that you can play with. The basic idea with this strategy is to let your express route render the response (using Handlebars):
Notice how
res.renderis used in this case, we are not sending a JSON response but the direct view result instead, which would look something like this:Notice also how we don't use Javascript here to send the request, just the default behavior of
<form>to make the request. This will cause the page to reload.Client Side Rendering Approach: Here is the same example slightly modified to use AJAX and
fetchAPI.Now our endpoint must return a JSON response that the client can use to react accordingly:
Then we let the client side Javascript handle the request and subsequent update of the DOM:
This will NOT cause the page to reload.