node js delete go to wrong route - node.js

I have app. I added DELETE feature by id. It looks like:
router.delete('/delete/:id', async function (req, res){
console.log("I'm here..");
try {
var id = req.params.id;
console.log("here!!!");
console.log(id);
var x = await Order.findByIdAndDelete(id).exec();
await x.save();
console.log("deleted!");
}catch (exc){console.log("Error during deleting!")}
res.redirect('back');
});
I have also form on my view (i use EJS).
<h1 class="mt-4">Dashboard</h1>
<p class="lead mb-3">Witaj <%= user.name %></p>
<% include ('./partials/messages.ejs')%>
<div class="row mt-5 float-left">
Wszystkie zamówienia
<table class="table float-left">
<tbody>
<% orders.forEach(function(value) { %>
<tr>
<form method="PATCH" action="/orders/order/<%=value.id%>">
<td>Id zamówienia <%= value.id %></td>
<td>Id klienta <%= value.userID %></td>
<td><input type="datetime-local" value= <%= value.dateStart %>
min=<%= value.dateStart %> max=<%= value.dateFinish %>> data rozpoczęcia</td>
<td><input type="datetime-local" value= <%= value.dateFinish %>
min=<%= value.dateStart %> max=<%= value.dateFinish %> >data zakończenia</td>
<td>Cena: <%= value.total %></td>
<td>Ilość dni <%= value.numberOfDays %></td>
<td> <button type="submit" class="btn btn-primary btn-lg">Edytuj i akceptuj</button></td>
</form>
**THERE IS A BUTTON** <form method="DELETE" action="/orders/delete/<%=value.id%>">
<td><button type="submit" class="btn btn-primary btn-lg" value="delete">Usuń</button></td></form>
<form method="POST" action="/orders/confirmOrder/<%=value.id%>">
<td><button type="submit" class="btn btn-primary btn-lg">Akceptuj</button></td></form>
</tr>
<% }) %>
</tbody>
</table>
</div>
Everything looks ok, and should work but it doesn't! Http request looks ok:
GET /orders/delete/607d4b8b06e1aee2dfa901be? 404 84.803 ms - 2580 - but WebStorm shows GET in console. Quite strange. Why? It should shows DELETE. Id of order is okay - taken from database.
Console.logs from route like "here!!!", "I'm here.." aren't showed by console. So I think something is wrong with route.
Moreover after action my layout stopping work because "user is not defined" - yes, i check if user is logged in the layout. It indicates that something is wrong - I have the same situation with other action, but I fixed it. Here I spent 4hrs at night and haven't fixed it yet.
Please, show me what is wrong here.
Kind Regards!

Problem solved! HTML <form> doesn't support DELETE! I was forced to use jquery to send delete request, or to change delete to post in route and in the view.
Now my view and route look:
<td><button type="submit" id="del-button" class="btn btn-primary btn-lg " value=<%=value.id%>>Usuń</button></td></form>
and route:
router.post('/delete/:id', async function (req, res){
//body here
});
I left my answer. Maybe someone use it in the future.

Related

How do I handle form data from a form with multiple fields in node?

I'm trying to build a page for people to RSVP for a wedding website. The guest types in their email address, and the page finds all guests that appear on that invitation and then generates a table:
<%- include("./partials/header") %>
<h2>
RSVP
</h2>
<div class="container">
<div class=" row justify-content-center">
<h5>Enter the email address that appears on your invitation.</h5>
</div>
<div class=" row justify-content-center">
<div id="form-findGuest">
<form action="/rsvp" method="GET">
<input type="text" name="searchEmail" placeholder="email">
<button type="submit">Submit</button>
</form>
</div>
</div>
<div class="row justify-content-center">
<form action="/rsvp" method="POST">
<table class="table">
<% for(i=0;i<guests.length;i++){ %>
<tr>
<td><%= guests[i].name %></td>
<% if(guests[i].isComing){ %>
<td>Attending</td>
<% } else { %>
<td>Not Attending</td>
<% } %>
<td>
<td>
<input type="checkbox">
</td>
</tr>
<% }%>
</table>
</form>
</div>
<div class="row justify-content-center">
Back
</div>
</div>
<%- include("./partials/footer") %>
The problem is that I don't know what to do with the post route. Basically I want to do something like this:
app.post("/rsvp", (req, res)=>{
// parse the req.body and for each rsvp that got sent here, add it to some array
// now use a for loop to iterate through that array and change the isComing value for each guest
res.redirect("index")
})
But I can't figure out how to parse the req.body when I don't know how many fields are going to be in the request- some email addresses may correspond to only one guest, but others may correspond to, say, five.
Add Body parser if not added already, and make use of
Object.keys
const express = require('express'),
app = express(),
bodyParser = require('body-parser');
// support parsing of application/json type post data
app.use(bodyParser.json());
//support parsing of application/x-www-form-urlencoded post data
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/rsvp", (req, res)=>{
var keys = Object.keys(req.body);
for(var i=0;i<keys.length;i++)
{
var guest=req.body[keys[i]];
// now use this to change the isComing value for each guest
}
res.redirect("index")
})

EJS just outputs the first found user in some cases

I'm Using mongoDB and ejs to display all my users in a table. The table also has some action buttons to delete the user and to change the users role. The buttons open a popup to change the role or to confirm the deletion. But EJS doesn't pass the users info into the popup. It works totally fine in the table, but not in the popup.
My EJS User Table with the Role change Popup:
<tbody>
<%users.forEach(function(users){%>
<tr>
<td><%=users.name%></td>
<td><%=users.username%></td>
<td><%=users.createdAt%></td>
<td><span class="badge label-table badge-<%=users.role%>"><%=users.role%></span></td>
<td><span class="badge label-table badge-<%=users.verifyEmailToken%>"><%=users.verifyEmailToken%></span></td>
<td>
<button type="submit" class="btn btn-xs btn-success" data-toggle="modal" data-target="#con-close-modal" name="changeRoleButton"><i class="remixicon-user-settings-line"></i></button>
</td>
</tr>
<div id="con-close-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Change <%=users.name%> Role</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<form class="" action="/changeuserrole" method="post">
<div class="modal-body p-4">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="field-1" class="control-label">User Role</label>
<select class="form-control" name="role">
<option>Partner</option>
<option>Admin</option>
<option>User</option>
</select>
</div>
<button type="submit" value="<%=users._id%>" name="userroleid" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<%});%>
</tbody>
Here is my app.js where I search for the users and pass them to EJS:
app.get("/users", function(req, res) {
if (req.isAuthenticated() && req.user.role === "Admin") {
User.find({}, function(err, foundUsers) {
if (err) {
console.log(err);
} else {
res.render("users", {
users: foundUsers,
name: req.user.name.replace(/ .*/, ''),
email: req.user.username,
});
}
});
} else {
res.redirect("/login");
}
});
All the <%=users...%> tags work inside the table, but not inside the popup divs. Inside the Popup it just displays the information from the first user in the Database, which is super strange.
I would be very thankful for any kind of help. Thanks!
Your ejs code is good. I think that the problem is the id of each modal.
For each user you generate a modal with id="con-close-modal", So all your modals have the same id. As a result, every submit button (all of them have the same data-target="#con-close-modal"), triggers the same modal, probably the first one.
I recommend you, give each modal a unique id like
<div id="<%= users._id %>" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;">
and give each submit button the right data-target attribute
<button type="submit" ... data-target="#<%= users._id %>"...></button>
I may be wrong, but since the popup is displaying only the info from the first user, you may need the specific user id to be apart of the link. I had a similar issue with a project, except it was a link to a new view instead of a pop up.
I hope this documentation may be of some help
https://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

Pass Dynamic Table Row Data to Modal Dialog via Delete Button

Context
I have a GET route that loads a .ejs file which adds rows of data to a table based on the length of the passed variable accounts. Here accounts is an array of dictionaries with the keys _id, type, nickname, rewards, balance, customer_id.
<% if (accounts.length > 0) { %>
<% accounts.forEach(account => { %>
<tr class="blue-grey lighten-3 account-row">
<th scope="row" class="account-id"><%= account._id %></th>
<td><%= account.type %></td>
<td><%= account.nickname %></td>
<td><%= account.rewards %></td>
<td>$<%= account.balance %></td>
<td><%= account.customer_id %></td>
<td>
<span class="table-remove">
<button type="button" class="btn btn-danger btn-rounded btn-sm my-0 remove" data-toggle="modal" data-target="#removeAccountModal">
Remove
</button>
</span>
</td>
<div class="modal fade" id="removeAccountModal" tabindex="-1" role="dialog" aria-labelledby="removeAccountModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="removeAccountModalLabel">You are about to remove this account</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
CAUTION!!!
<br>
<br>
Removing this account will delete all the data associated with it. You must visit a local branch to retrieve any monies left residing in the account.
</div>
<div class="modal-footer">
<button type="button" class="btn grey darken-1" data-dismiss="modal">Cancel</button>
<form id="remove-form" action="/" method="POST">
<button id="removeAccount" class="btn btn-primary" type="submit">Remove Account</button>
</form>
</div>
</div>
</div>
</div>
</tr>
<% }) %>
Issue
There is a delete button on each row of data that opens a Bootstrap modal confirming that the user wants to indeed delete that row's data. Upon a submit in the modal, this will initiate a DELETE request in the form of a form element.
I want to pass the selected row's associated account._id variable so that I can modify the form's action attribute to /<%= account._id %>/?_METHOD=DELETE.
I am having trouble accessing the modal's parent window DOM elements.
My jQuery code is as follows:
<script>
$(document).ready(function () {
console.log("loaded and ready to go!"); // works as expected
var $remove = $(".remove"); // works as expected
console.log($remove.length); // works as expected
$(".remove").click(() => {
console.log("I'm in the modal!") // works as expected
var $parent = $(this).parent().closest("tr"); // find row
console.log($parent); // w.fn.init [prevObject: w.fn.init(0)]
var $account_id = $parent.find("th"); // find account id
console.log($account_id); // w.fn.init [prevObject: w.fn.init(0)]
console.log($account_id.text()); // returns blank type string
$("#remove-form").prop("action", "/" + $account_id + "?
_method=DELETE"); // change action attribute to account id value
})
});
</script>
Methods tried:
console.log(window.parent.$(this).closest("tr"));
// returns w.fn.init [prevObject: w.fn.init(1)]
you have placed the modal inside the forEach loop. So you dont have only one modal, instead you have one for each row. The problem here is that you assigned the same id removeAccountModal for all of them. Try to assign a unique id for each modal, for example:
<div class="modal fade" id="<%= account._id %>" tabindex="-1" role="dialog" aria-labelledby="removeAccountModalLabel" aria-hidden="true">
and on each delete button replace data-target with
data-target="#<%= account._id %>"
So you have unique modals and now you can place <%= account._id %> directly in action attribute
Thank you #dimitris tseggenes for pointing me in the right direction.
The unique ids for each modal div was imperative here. It turns out the id attribute value must not solely consist of numbers.
I refactored the id and target-data attributes to look like the following, respectively:
id="account<%= account._id %>id"
data-target=#account"<%= account._id %>id"
I have learned the following from the responses in this post:
Don't forget to make modal divs unique if you will have multiple modals, i.e. in a table and to alter the action element respectively using the data-target attribute
id attributes must not solely contain numbers

what can this ejs be look like in jade?

I am learning how to build simple CRUD web application and instruction is written in ejs. However, I just start learning jade and do not know how can I convert this ejs code to jade.
<% layout( 'layout' ) -%>
<h1 id="page-title"><%= title %></h1>
<div id="list">
<form action="/create" method="post" accept-charset="utf-8">
<div class="item-new">
<input class="input" type="text" name="content" />
</div>
</form>
<% todos.forEach( function ( todo ){ %>
<div class="item">
<a class="update-link" href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
<a class="del-btn" href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</div>
<% }); %>
</div>
and this is what I've done,
extends layout
h1#page-title= title
#list
form(action="/create" method="post" accept-charset='utf-8')
.item-new
input(type='text' name='content')
So you want to know how to write that second part in jade. Each is one of Jades primary methods for iteration. And your code could be written something like this:
each todo in todos
.item
a(class="update-link" href="/edit/"+todo._id title="Update this todo item")= todo.content
a(class="del-btn" href="/destroy/"+todo._id title="Delete this todo item") Delete
Here is a link for jades documentation about iterations: Jade Iterations.

Uncaught ReferenceError: JST is not defined on Sails 0.11

while using Sails I get this error Uncaught ReferenceError: JST is not defined on the chrome console everytime this line of code:
JST['SailsApp/assets/templates/addUser.ejs']( obj )
The content of the addUser.ejs file is:
<tr data-id="<%= user.id %>" data-model="user">
<% if (user.online) { %>
<td><span class="fa fa-circle fa-lg fa-circle-green"></span></td>
<% } else { %>
<td><span class="fa fa-circle fa-lg fa-circle-red"></span></td>
<% } %>
<td><%= user.id %></td>
<td><%= user.name %></td>
<td><%= user.title %></td>
<td><%= user.email %></td>
<% if (user.admin) { %>
<td> <span class="glyphicon glyphicon-king" > </span></td>
<% } else { %>
<td> <span class="glyphicon glyphicon-user" > </span></td>
<% } %>
<td>Show </td>
<td>Edit </td>
<td>
<form action="/user/destroy/<%= user.id %>" method="POST">
<input type="hidden" name="_method" value="delete"/>
<input type="submit" class="btn btn-sm btn-danger" value="Delete"/>
<input type="hidden" name="_csrf" value="<%= _csrf%>" />
</form>
</td>
</tr>
The compiled .jst file is not appearing in .tmp/public folder, and if I run sudo grunt jst manually then it's created but the chrome console continues giving the same error.
Any help would be much appreciated.
Thanks in advance!
I had the same problem recently. Under 'tasks/register/compileAssets.js', try moving jst:dev below copy:dev.
module.exports = function (grunt) {
grunt.registerTask('compileAssets', [
'clean:dev',
'less:dev',
'copy:dev',
'jst:dev',
'coffee:dev'
]);
};
It seems that the jst:dev task is looking for the template files in the .tmp/ dir, but they aren't moved from assets/ until the copy task executes. You'll see this if you run grunt and get an error like:
Running "jst:dev" (jst) task
>> Destination not written because compiled files were empty.

Resources