I have a form and a basic route that sends data to a database once the submit button has been clicked. Here is the code for the form I am working with:
<form action="/posts" method="POST">
<label>
<p>Enter your post title: </p>
<input type = "text" id= 'title' name='title' value='enter name here'>
<p>Enter your post description:</p>
<input type = 'text' id = 'description' name ='description' value = 'your description here'>
<input type="submit" value = 'OK'>
</label>
</form>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
And then here is the code that gets the data and sends it off:
router.post('/', (req, res) => {
const post = new Post({
title: req.body.title,
description: req.body.description
});
post.save().then(data=> {
res.json(data);
})
.catch(err => {
res.json({message: err });
});
res.render('submission_complete.ejs');
});
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
After doing some research, I've learned that res.json() ends the response and so does res.render(). The issue is, I want to send the data and then render a new page that tells the user that the data has been successfully rendered. How do I go about achieving this? The code works, but it throws an error, "UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
CodePudding user response:
Ok so, if you want to render an ejs template and also send data to the client(with that template), express allow us to do that simply by calling the response render
method and passing not only the name of the template, but also some data to it in the second argument. In your case it would be:
router.post('/', (req, res) => {
const post = new Post({
title: req.body.title,
description: req.body.description
});
post.save().then(data=> {
// Now i'm calling the render method and passing to it:
// - the name of the template i want to render(without extension if you called the app.set('view engine', 'ejs'))
// - an object containing the data property which is the response you get after the post has been saved
res.render('submission_complete', { data /* you can also write it as { post: data } */ });
})
.catch(err => {
res.json({message: err });
});
});
Once you've done that in your template you can simply access these variables, so it would be(i guess what you want to do):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>POST SAVED SUCCESFULLY</h1> <!-- or whatever -->
<div>
<!-- it would be data.title if you passed '{ data }' as the second argument -->
<%= post.title %>
<br>
<!-- same as above, (data or post).description depending on what you passed as the second argument -->
<%= post.description %>
</div>
</body>
</html>
You can also just call
res.render('submission_complete', data);
And access its properties individually
<div>
<%= title %>
<br>
<%= description %>
</div>
Said that, i'll add your code with some improvements just to make it cleaner
// DISCLAIMER
// I'm calling the render method and not passing the file extension because
// i'm assuming you called 'app.set('view engine', 'ejs');' in the file where you
// declare the app variable
router.post('/', async (req, res) => {
// if you don't want to check the body of your request because you know you can receive only the right properties you can do all this stuff in just few lines
try {
const post = await new Post(req.body).save();
res.render('submission_complete', post);
} catch (e) { res.json({ message: err.message }); }
});