Pass Dynamic Table Row Data to Modal Dialog via Delete Button - node.js

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

Related

Retrieving value from input in EJS with NodeJS

Here is my EJS code:
<form action="/" method="POST">
<% items.forEach((item)=>{ %>
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onchange="this.form.submit()">
<p><%= item.name %></p>
</div>
<% }) %>
</form>
And here is the app.js:
app.post('/',(req,res)=>{
console.log(req.body.checkbox);
})
My questions are:
1) Why does it only log the value of the selected checkbox (assumed that there are 3 items) instead of 3 values, is it because of the post method only post the checkbox's value that has the property checked == true?
2) If I add another checkbox like below, why does it return undefined if I try to console.log(req.body.item_id)?
<form action="/" method="POST">
<% items.forEach((item)=>{ %>
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onchange="this.form.submit()">
<p><%= item.name %></p>
<input type="checkbox" name="item_id" value="<%=item._id%>">
</div>
<% }) %>
</form>
3) I've tried to do the same thing above but this time it is using input type of text. Why does the console.log(req.body.item_id) show 3 values instead of 1 (which corresponds with the selected checkbox like in question 1)?
<form action="/" method="POST">
<% items.forEach((item)=>{ %>
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onchange="this.form.submit()">
<p><%= item.name %></p>
<input name="item_id" value="<%=item._id%>">
</div>
<% }) %>
</form>
All response are appreciated. Thank you!
That is the default behaviour of the checkbox, it only gets
passed as data if its checked, if it doesn't get passed, we can
assume it wasn't checked
However if you still want the checkbox in data even if it was not checked, one hack would be
Create a hidden element with same name as your checkbox & set value as 'off'
Before submit, if the checkbox is checked, disable the hidden input element
this will give your checkbox value 'on' when checked & 'off' when its not
As mentioned above, it will only added to formdata if its
checked, when its not checked, the element is not added to formdata
at all, so you will get undefined if its not checked
Again this is default behaviour of input types, the reason it
does not do same for checkbox is because, maybe you are only
selecting 1 checkbox with same name before submit, if you do select
multiple checkbox with same name in checkboxes, you'll get same
result for checkboxes too, but due to the reason mentioned in first
answer, it only get's us the element selected which is 1 in your
case

NodeJS send to POST also unchecked input

I have a form in which I iterate an input checkbox field n times, as follows:
<form class="" action="" method="post">
<% availableDirezione.forEach((direzione, index) => { %>
<p>
<input class="checkbox" type="checkbox" name="checkArray" id="check0" value="1">
<span><%= direzione.usr_udf_direzione %> (<%= direzione.usr_udf_dirid %>) </span>
</p>
<% }) %>
<button type="submit" class="btn btn-primary float-right mb-5">Add Mapping</button><br>
</form>
I would like that in the post method, even the unchecked values are passed in the checkArray object.
Let's suppose the forEach iterates 5 times, and only the first and last checkbox are checked, I would like that the checkArray object is as following:
...
body : {
checkArray : [1,0,0,0,1]
}
...
I tried adding the hidden checkbox with a 0 value. It fills the checkArray object 5 times but in wrong error. Any tips?
Sorry but is not possible. This is not due to a Node.js limitation but rather a HTML standard. See second 'note' block in
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox (under value section)
Hence you may try to fight against it with some front-end js code, but I wont suggest it. What you need to do is have a mean to reconstruct the array at the end like this:
<form class="" action="" method="post">
<% availableDirezione.forEach((direzione, index) => { %>
<p>
<input class="checkbox" type="checkbox" name="checkArray" id="check0" value="<%= index %>">
<span><%= direzione.usr_udf_direzione %> (<%= direzione.usr_udf_dirid %>) </span>
</p>
<% }) %>
<button type="submit" class="btn btn-primary float-right mb-5">Add Mapping</button><br>
</form>
Then when you receive your POST:
availableDirezione.map((direzione, index) => checkArray.indexOf(index) !== -1 0 : 1)
This will produce a [0, 1, 1, 0, 0 ... ] array
availableDirezione.map((direzione, index) => checkArray.indexOf(index) !== -1 direzione : none)
Were this will produce a [none, {direzione}, {direzione}, ... ] array
The trick is to use value as your value media and set there something adequate. In this case i used the index of the value inside availableDirezione array, but you may even put set it to something even more meaningful like a unique id present in your direzione objects.

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 Data from MongoDB to Bootstrap Modal Using EJS

I'm building an express app which comprises ejs and bootstrap for rendering views, node and express for server side and mongodb for storage.
I have a form (contained in a modal) in which I can post data. After submitting the form, the values are saved to mongodb.
Here's the snippet for edit:
<td><%= cats.image %></td>
<td><a type="button" data-toggle="modal" data-target="#editCategory" href="#">Edit</a>
// without using the modal above, I'll have to create a new page to edit only one item
// I think it's a waste of page space and bad UX, so I am going with the modal
<!--<a href="/admin/categories/edit-category/<%= cats._id %>">--> // I want to get the values from this id and show in the modal
<!--<button type="button" class="btn btn-primary btn-sm">Edit</button>-->
<!--</a>-->
</td>
When I click on the edit button, I want to get the id of the item and retrieve it's values to display in the modal.
Here's the full modal view:
<!-- Edit Category Modal -->
<div class="modal fade" id="editCategory" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="/admin/categories/edit-cateory/<%= %>" method="post" enctype="multipart/form-data">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button>
<h4 class="modal-title" id="myEditCategoryLabel">Edit Category</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>Title</label>
// this value should come from the category id but don't know how to pass it
<input value="<%= %>" name="title" class="form-control" type="text" placeholder="Category Title"> <br>
<label>Upload Image</label>
<input class="form-control selImg" type="file" accept="image/*" onchange="showImage.call(this)">
<img src="#" class="imgPreview" style="display: none; height: 100px; width: 100px">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Edits</button>
</div>
</form>
</div>
</div>
</div>
How can I pass the data from mongodb to the modal using ejs?
Any help will be much appreciated.
Store your ID somewhere: hidden inputs or data-attributes.
<tr data-id="<%= cats.id %>">
<td><%= cats.image %></td>
<td>
<a type="button" class="edit" href="#">Edit</a>
</td>
</tr>
Then, create an onclick event handler such that, upon clicking on the button,
it will fetch the extra data via AJAX and open the modal dynamically after you get a response.
$('a.edit').click(function (e) {
e.preventDefault();
var tr = $(this).closest('tr'),
modal = $('#editCategory');
// make AJAX call passing the ID
$.getJSON('/admin/categories/get-cateory/' + tr.data('id'), function (data) {
// set values in modal
modal.find('form').attr('action', '/admin/categories/get-cateory/' + tr.data('id') );
modal.find('[name=title]').val( data.title );
// open modal
modal.modal('show');
});
});
You will need to create a route for fetching a single object (I'll leave that to you)
app.get('/admin/categories/get-cateory/:id', function (req, res) {
// TODO: fetch data using passed ID
// once you have the data, simply send the JSON back
res.json(data);
});

how can I show car's details When click on their names

I am making a website with node.js and I am new ,I want to learn a method if there is.I list cars using ul and when I click on a car name i want to show car's details. How can I do it.
html
<template name="vehicles">
<section id="vehicles" class="container">
<div class="row">
<div class="col-md-12">
<h2 class="title wow fadeInDown" data-wow-offset="200">Vehicle Models - <span class="subtitle">Our rental fleet at a glance</span></h2>
</div>
<!-- Vehicle nav start -->
<div class="col-md-3 vehicle-nav-row wow fadeInUp" data-wow-offset="100">
<div id="vehicle-nav-container">
<ul class="vehicle-nav">
{{#each showcarnames}}
<li class="active">{{aracmarka}}<span class="active"> </span></li>
{{/each}}
</ul>
</div>
<div class="vehicle-nav-control">
<a class="vehicle-nav-scroll" data-direction="up" href="#"><i class="fa fa-chevron-up"></i></a>
<a class="vehicle-nav-scroll" data-direction="down" href="#"><i class="fa fa-chevron-down"></i></a>
</div>
</div>
<!-- Vehicle nav end -->
<!-- Vehicle 1 data start -->
<div class="vehicle-data" id="vehicle-1">
<div class="col-md-6 wow fadeIn" data-wow-offset="100">
<div class="vehicle-img">
<img class="img-responsive" src="img/vehicle1.jpg" alt="Vehicle">
</div>
</div>
<div class="col-md-3 wow fadeInUp" data-wow-offset="200">
<div class="vehicle-price">$ 37.40 <span class="info">rent per day</span></div>
<table class="table vehicle-features">
<tr>
<td>Marka</td>
<td>{{carmark}}</td>
</tr>
<tr>
<td>Model</td>
<td>{{carmodel}}</td>
</tr>
</table>
<span class="glyphicon glyphicon-calendar"></span> Reserve now
</div>
</div>
js
Template.vehicles.helpers({
showcarnames: function() {
return cars.find();
}
});
I would approach this problem using Session. You could target the data using a click event:
Template.vehicles.events({
'click .vehicle-nav li': function(){
Session.set('selected-vehicle', this._id); // or however you id the docs in your app.
}
});
then create an event helper that gets the selected doc and returns it to the template.
Template.vehicles.helpers({
getSelectedVehicle: function() {
var selectedId = Session.get('selected-vehicle');
return cars.findOne(selectedId);
},
});
Session is a great and simple tool to manage user state, like what vehicle they have selected.
Finally, you would then need to get the values in your template somewhere
<!-- html-->
{{#if getSelectedVehicle}}
{{#with getSelectedVehicle}}
<!-- mark up, when using with you can access doc atts directly. -->
{{/with}}
{{else}}
<!-- tell the user to make a selection -->
{{/if}}
using with in this context can lead to more readable markup. But there are other ways to achieve the same result.
To recap, at a high level, You are targeting the users interactions with the UI, to set a global variable as a way to simplify managing state. Be sure to check out Session in the meteor docs, its very simple and powerful. (the above code is not tested, but hopefully conveys the idea)

Resources