Add Update Functionality - javascript

Sorry if this is super obvious, but I'm new to coding. I'm using node.js, express, mongoose and mongodb to try and add an update function to my app to make it CRUD by adding an edit button. Whenever I click the edit button though it still just deletes the item as if I were clicking the checkbox. I'm thinking it's because I'm calling the item from the same form of "pending items" but it seems like my update code isn't even registering as my console.logs for //Edit items aren't logging.
I want it to identify the item by its id when its edit button is submitted (then put the item in the newTask input to be edited and resubmitted as an update but I haven't figured out how to link those 2). I know the code is wonky, I'm just trying to figure out how to put this together, so thanks for any help!
<div class="box">
<!-- Item add function -->
<% newListItems.forEach(function(item){ %>
<!-- Form for pending items -->
<form action="/update" method="post">
<!-- Items -->
<div class="item">
<input type="checkbox" name="checkbox" value="<%=item._id%>" onChange="this.form.submit()">
<button type="submit" class="editItmbtn" name="editItembtn">E</button>
<p><%=item.name%></p>
</div>
<input type="hidden" name="listName" value="<%= listTitle %>"></input>
</form>
<% }) %>
<!-- End pending items -->
<!-- Form to add items -->
<form class="item" action="/" method="post">
<input type="text" name="newTask" id="id" placeholder="Add new task..." autocomplete="off">
<button type="submit" name="list" value="<%= listTitle %>">+</button>
</form>
</div>
''//Requirements
const express = require("express");
const session = require("express-session")//for sessions
const favicon = require("serve-favicon"); //for favicon
const path = require("path");// for favicon
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser"); //for sessions
const mongoose = require("mongoose"); const _ = require("lodash");
const MongoStore = require("connect-mongo")(session);
const app = express();
// Edit items
app.put("/update", function(req, res) {
const itemName = req.body.newTask;
const taskID = req.body.editItmbtn;
const userInput = req.body.id;
Item.useFindAndModify(taskID), {
$set: {"/update": userInput}}, {new: true},
(err, result) => {
if (err) {
console.log("ERROR");
} else {
res.redirect("/");
res.render("list", {
listTitle: "Tasks",
newListItems: foundItems
});
}
}
});
// Delete checked items
app.post("/update", function(req, res) {
const checkedItemId = req.body.checkbox;
const listName = req.body.listName;
if (listName === "Tasks") {
Item.findByIdAndRemove(checkedItemId, function(err) {
if (!err) {
console.log("Successfully deleted checked item.");
res.redirect("/");
}
});
} else {
List.findOneAndUpdate({
name: listName
}, {
$pull: {
items: {
_id: checkedItemId
}
}
}, function(err, foundList) {
if (!err) {
res.redirect("/" + listName);
}
});
}
});

In your form you are using the method="post" which means that the request will be submitted to your app.post("/update" ..... ) , that is why you always land there, where you delete your item.
You have to do two things:
Change the app.put('/update' ...) to app.post('/update' ...)
Change the app.post('/update' ... which is meant to delete the item to something semantically more relevant like maybe app.post('/delete' ... and change the frontend with which you delete method respectively.

You have app.put("/update", function(req, res) in Express but <form action="/update" method="post"> in your form, which matches your delete route.
For your second question, there are many ways to populate your item form in Javascript, but there might be an even more efficient way if you tell us if you're using a framework.

Your form always submit the http request with the "POST" method, but in your code the app.post("update") deletes the item

Related

How to add a button with an Update function using Node and Mongoose

I have created a mongoose db with events, each event is inside a bootstrap card. I am trying to figure out a way how can I redirect admin to a page where he can update the event-card and saved to my mongoose db.
The issue I have is that I already have a form for that card with a delete action and I need to be able to have another one. What I thought is to add a link which will redirect to an update_page.ejs where admin will can make changes to the form of that event and save them.
Code for admin.ejs page
<% events.forEach( function (event) { %>
<form class="form-events" action="/delete" method="post">
<div class="card col-lg-3" style="width: 18rem;">
<img class="card-img-top">
<div class="card-body">
<h5 class="card-title"><%= event.title %></h5>
<p class="card-text"><%= event.description %></p>
<p class="card-text"><%= event.date %> </p>
<p class="card-text"><%= event.capacity %></p>
Update
<button name="deleteBtn" value="<%=event._id%>" type="submit" class="btn btn-danger">Delete</button>
</div>
</div>
<% }); %>
</form>
Currently the update button redirects here:
where is basically a creation of an event.
A simple solution I thought is just to add another button near the create page like update.
So my first problem is that how can I after redirecting from my admin.ejs to create_page.ejs
have the from completed by the event pressed otherwise the form contents will be empty, and which app.() method I must use ( POST, PATCH ) and finally is it even possible to add 2 actions to one form?
app.js code
//Create new event route.
app.post("/create_event", function (req, res) {
const event = new Event({
title: req.body.ev_title,
description: req.body.ev_desc,
date: req.body.ev_date,
capacity: req.body.ev_cap,
});
event.save(function (err) {
if (err) {
console.log(err);
} else {
res.redirect("/admin");
}
});
});
// Delete an event
app.post("/delete", function (req, res) {
const deletedItemId = req.body.deleteBtn;
Event.findByIdAndDelete(deletedItemId, function (err) {
if (!err) {
console.log("Successfully deleted");
res.redirect("/admin");
} else {
console.log(err);
}
});
});
P.S my solution for the DELETE route is correct?
I am sorry for all the specifics, but I am totally new to this and have this project deadline coming up
googled all this method but the docs seemed a bit too technical right now for me.
You can simply add a another route to node application like
// Update an event.
app.post("/update/:id", function (req, res) {
const _id = req.params.id;
const event = new Event({
title: req.body.ev_title,
description: req.body.ev_desc,
date: req.body.ev_date,
capacity: req.body.ev_cap,
});
event.findOne({ _id }, '_id title description date capacity', function (err, data) {
if (err) {
console.log(err);
} else {
res.render('path to create_data.ejs', { event : data });
}
});
});
In the create_data.ejs file, fill all the form elements with value of the event like value="<%= event.title %>".
Note : If you're making above changes in create_data.ejs file, make sure to pass a blank event property in the create_event route as well like
// Create an event.
app.post("/create", function (req, res) {
res.render('path to create_data.ejs', { event : {});
});
Now,
Add a condition on create button in create_event.ejs file like
<% if(_event.id){ %>
... Update button to save the updated event
<% }else{ %>
... Create button to create a new event
<% } %>
Finally, In your admin.ejs file change
Update
to
Update
Hope this answers your question.

Mongoose not updating database with findByIdAndUpdate?

I am trying to update my database using findByIdAndUpdate through an HTML form, which works for all but the nested data (ePIMS, codeVersion, and region all update with no problem). When I do console.log(req.body.environment.instance), it outputs the data I typed in like it's going through correctly, but for some reason the mongoDB does not update with the information. Can anyone figure out what I'm doing wrong?
mongoose schema:
var environmentSchema = new mongoose.Schema({
ePIMS: String,
codeVersion: String,
region: String,
/*instance and testEnv don't seem to update in the database*/
HCHC: {
instance: String,
testEnv: String
}
});
form I'm using to update:
<form action="/environments/<%= environment._id %>?_method=PUT" method="POST">
<input class="form-control" type="text" name="environment[ePIMS]" placeholder="ePIMS" value="<%= environment.ePIMS %>" />
<input class="form-control" type="text" name="environment[region]" placeholder="Region" value="<%= environment.region %>" />
<input class="form-control" type="text" name="environment[instance]" placeholder="HCHC Instance" value="<%= environment.instance %>" />
<input class="form-control" type="text" name="environment[testEnv]" placeholder="Test Environment" value="<%= environment.testEnv %>" />
<button class="btn btn-primary">Submit</button>
</form>
edit and update routes:
//Edit environment route
router.get("/environments/:id/edit", function(req, res){
Environment.findById(req.params.id, function(err, foundEnvironment){
if(err){
res.redirect("/");
} else {
res.render("edit", {environment: foundEnvironment});
}
});
});
//Update environment route
router.put("/environments/:id", function(req, res){
Environment.findByIdAndUpdate(req.params.id, req.body.environment, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
//console.log(req.body.environment.instance)
}
});
});
UPDATE: SOLUTION
Thank you Nayan for your help!
I changed the update route like so:
//Update environment route
router.put("/environments/:id", function(req, res){
var data = {
HCHC : {
instance: req.body.instance,
testEnv: req.body.testEnv
}
}
Environment.findByIdAndUpdate(req.params.id, {$set: data}, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
}
});
});
You are sending different body structures and setting it directly in findOneAndUpdate() so it didn't work, where the structure is different.
The possible solution you can apply is to change the body of the request to look something like this:
"environment" : {
"ePIMS" : value,
"codeVersion" : value,
"region" : value,
"HCHC": {
"instance" : value,
"testEnv" : value
}
}
Or you can put the two thing instance and testEnv out of HCHC if you want.
Either way make sure you have same structure if you are passing the body directly in the function.
Another solution
create a local variable to fix the structure and pass that in function like:
router.put("/environments/:id", function(req, res){
let body = req.body.environment
let bodyData = {
ePIMS: body.ePIMS,
codeVersion: body.codeVersion,
region: body.region,
HCHC: {
instance: body.instance,
testEnv: body.testEnv
}
}
Environment.findByIdAndUpdate(req.params.id, bodyData, function(err, updatedEnvironment){
if (err) {
res.redirect("/environments");
} else {
res.redirect("/environments");
//console.log(req.body.environment.instance)
}
});
});
this solution worked for me:
router.patch('/tasks/:id', async (req,res)=>{
try{
const task= await Task.findByIdAndUpdate(req.params.id,req.body,{new:true,runValidators:true})
if(!task)
{
res.status(404).send()
}
res.send(task)
}
catch(e)
{
res.status(500).send()
}
})
however,my final solution included validation was inplementing update without using findByIdAndUpdate:
router.patch('/tasks/:id', async (req,res)=>{
//validate update operation
const updates=Object.keys(req.body)
const allowedUpdates= ['description','completed']
const isInvalidOperation= updates.every((update)=>allowedUpdates.includes(update))
if(!isInvalidOperation)
{
return res.status(400).send({error:'invalid updates'})
}
try{
const task= await Task.findById(req.params.id)
updates.forEach((update)=>task[update]=req.body[update])
await task.save()
if(!task)
{
res.status(404).send()
}
res.send(task)
}
catch(e)
{
res.status(500).send()
}
})

How to use onchange to render ejs via a select element

I am using Node.js to render EJS files. I want a user to fill out a log form and have their data sent to a database. The initial part of the form is in log.ejs, is rendered via log.js, and is incomplete. When the user selects an option, I want the rest of the form to show up. I want each option to render a slightly different form (i.e. easyForm, longrunForm, etc.) and complete the original form while still using log.ejs. I've tried to accomplish this by adding <%- include('logForms/easyForm.ejs') -%> after the incomplete form in log.ejs and have tried doing this several different ways with the <select> elements onclick="" attribute. I have not come up with functioning code and I do not know the best way to go about this with EJS format. If I manually add <%- include('logForms/easyForm.ejs') -%> after the incomplete form in log.ejs, then the form loads and I can save data successfully to my database. Any help would be appreciated.
log.ejs
<%- include('partials/header.ejs') -%>
<!--Form begins but has no closing -->
<form class="" action="/log" method="post">
<div class="form-group">
<label for="">Date</label>
<input type="date" name="workoutDate" class="">
<label for="">Time</label>
<input type="time" name="timeOfDay" placeholder="Time of day hh:mm">
<select class="" id="workoutSelect" name="workoutType" onchange="">
<option disabled selected value>-- select an option --</option>
<option value="run">Run</option>
<option value="longRun">Long run</option>
<option value="shakeout">Shakeout</option>
<option value="workout">Workout</option>
<option value="race">Race</option>
<option value="xTrain">X-Train</option>
</select>
</div>
<!--This is where specific logForm is used to add appropriate fields and close the form -->
<%- include('partials/footer.ejs') -%>
log.js
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Data = require('../models/data');
router.get('/', (req, res, next) => {
res.render('log');
});
router.post('/', (req, res, next) => {
const data = new Data ({
date: req.body.workoutDate,
timeOfDay: req.body.timeOfDay,
type: req.body.workoutType,
otherData: req.body.otherData,
});
data.save()
.then(results => {
console.log(results);
res.render('log');
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
module.exports = router;

Implementing search in js

I've been trying to implement some searching features. I need the program to read search area and redirect the user to the page with results. Here's my search.ejs
<script>
function executeSearch($this) {
console.log('button clicked');
let request_data = document.getElementById('search-area').value;
console.log("Request data : " + request_data);
$.post("search", {subtitle: request_data}, function(json) {
console.log('requested access completed');
})
}
</script>
<input id="search-area" type="search" value="" placeholder="Enter movie title" />
<button onclick="executeSearch(this)" class="details-button">Search</button>
<ul class="search-list">
<% for(let i=0; i< movieList.length; i++) { %>
<li class="single-search-result">
<h3 class="text-light">
<%=movieList[i].title%>
</h3>
<br>
</li>
<% } %>
</ul>
Here's the code handling the request :
app.post('/search', (req, res) => {
movie_controller.getBySubTitle(req.body.subtitle)
.then(result => {
res.render('search', {
movieList: result
});
})
.catch(err => {
console.log(err);
res.render(500);
})});
By using console.log, I've determined that there are no issues in transferring required data, however I can't render 'search' page after getting data from getBySubtitle(). Any ideas what can be causing this? Thank you in advance.

Issues submitting data to MongoDB using Node.js

I am attempting to send form data from a page to MongoDB in Node.js.
The issue I am running into is when I am clicking the 'Add Group' button to submit the data.. the page tries to complete the request but seems to get stuck when trying to push the data to the database. So it then is just sitting there, stuck, trying to take the inputted data and place it into the database.
Here is my Group Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
path = require('path');
var GroupsSchema = new Schema({
groupName: {type: String}
});
GroupsSchema.virtual('uniqueId')
.get(function(){
return this.filename.replace(path.extname(this.filename), '');
});
module.exports = mongoose.model('Groups', GroupsSchema);
Here is my Group Controller:
var Models = require('../models');
module.exports = {
index: function(req, res){
var viewModel = {
groups: []
};
Models.Group.find({}, function(err, groups){
if(err){
throw err;
}else{
viewModel.groups = groups;
res.render('addGroup', {title: 'Admin Add Product Group', adminloggedin: true, subtitle: 'Add a Group', underheaderp: ''});
}
});
},
create: function(req, res){
var saveGroup = function(){
Models.Group.find({}, function(err, groups){
if(groups.length > 0){
saveGroup();
}else{
Models.Group.find({},function(err, groups){
if(err){
throw err;
}else{
var newGrp = new Models.Group({
groupName: req.body.groupname
});
newGrp.save(function(err, group){
console.log('Successfully inserted Group');
res.redirect('admin/addGroup');
});
}
});
}
});
};
saveGroup();
}
};
My current Routes:
var express = require('express'),
router = express.Router(),
addGroup = require('../controllers/addGroup');
module.exports = function(app){
router.get('/admin/addGroup', addGroup.index);
router.post('/admin/addGroup', addGroup.create);
app.use(router);
}
And my addGroup handlebars page
<!-- Add a Product Group Form -->
<div class="row">
<div class="col-md-6">
<form action="/admin/addGroup" method="post">
<fieldset class="form-group">
<label for="newGroupName">Group Name:</label>
<input type="text" class="form-control" name="groupname">
</fieldset>
<fieldset class="form-group">
<label for="groupImageFolder">Image Folder Name:</label>
<input type="text" class="form-control" name ="groupImageFolder">
</fieldset>
<button type="submit" class="btn btn-success" type="button">Add Group</button>
</form>
</div>
</div>
Unfortunately, I have yet to find a great way to debug my applications as I am still a new programmer. Any recommendations would be great as well.
The problem must be in my controller :create
Possibly where I am defining my var newGrp and trying to set it to my group models?
How can I fix this to make it so it saves the inputted data to MongoDB?
Any help is greatly appreciated.

Categories