I can't change the value in the database - array - node.js

I need to update the comment status, i.e. the name of the "approve" field. To this end, I created the AJAX script also the backend seems correct because it receives a message after clicking the button. I don't know why I can't save the "approve" value to the database. Please help. I use NodeJS, MongoDB, Express, and EJS for frontend.
My database:
My frontend:
<% post.comments.forEach(function (comment) { %>
<tr>
<td> <%= comment._id %> </td>
<td> <%= comment.username %> </td>
<td> <%= comment.comment %> </td>
<form method="post" onsubmit="return doApprove(this);">
<input type="hidden" name="post_id" value="<%= post._id %>" />
<input type="hidden" id="comment_id" name="comment_id">
<td> <input class="approve-comment" type="checkbox" name="approve" value="true">
<button class="btn btn-info" value="Approve"/>
</td>
</form>
<% }) %>
</tr>
</table>
</div>
<script>
function doApprove(form) {
var formData= {approve:form.approve.value};
$.ajax({
url: "/do-edit-comment",
method: "POST",
data: formData,
success: function (response) {
formData._id =response._id;
alert(response.text);
}
});
return false;
}
</script>
My backend:
app.post("/do-edit-comment", function (req,res) {
blog.collection("posts").updateOne({
"_id": ObjectId(req.body.id),
"comments._id": ObjectId(req.body.id)
},{
$set: {
"comments": {approve: req.body.approve}
}
}, function (error,document) {
res.send({
text: "comment approved"
});
});
});

To update a single element from an array, what you need is the $[<identifer>] array update operator. For the scenario described in your question, the update query in the server should be something like this:
blog.collection("posts").updateOne(
{ "_id": ObjectId(req.body.post_id), },
{ $set: { "comments.$[comment].approve": req.body.approve } },
{ arrayFilters: [{ "comment._id": ObjectId(req.body.commentId) }] },
function (error, document) {
res.send({
text: "comment approved"
});
}
)
EDIT(Front-end issues/fix)
So I figured there are some issues with the front end code too. This edit attempts to explain/fix those issues.
Issues:
1. The backend expects a commentId in the request object(req.body.commentId) to be able to identify the comment to update but you are not sending that from the front end.
2. The backend needs the id of the post to uniquely identify the post to update, but you are not sending that from the front end.
3. The approve value in the form data sent from the front-end is a string and it would always be "true". This is not what you want, you want to send a boolean value(true or false) depending on if the checkbox is checked or not.
Fix:
Update the form template to this:
<form method="post" onsubmit="return doApprove(event);">
<input type="hidden" name="post_id" value="<%= post._id %>" />
<input type="hidden" id="<%= comment._id %>" name="comment_id" />
<td> <input class="approve-comment" type="checkbox" name="approve" value="true">
<button class="btn btn-info" value="Approve"/>
</td>
</form>
Changes:
- The doApprove handler attached to the submit event of the form is now called with event instead of this.
- I updated the value of the id attribute for the input#name="comment_id" element to the actual comment._id value.
And in the script tag, update the doApprove function to this:
function doApprove(event) {
event.preventDefault();
const form = event.target;
var formData = {
approve: form.approve.checked, // form.approve.checked would be true if the checkbox is checked and false if it isn't
post_id: form.post_id.value, // The value of the input#name=post_id element
commentId: form.comment_id.id, // The id attribute of the input#name=comment_id element
};
$.ajax({
url: "/do-edit-comment",
method: "POST",
data: formData,
success: function (response) {
formData._id =response._id;
alert(response.text);
}
});
return false;
}
I hope that helps.

Related

Why isn't my title being inserted into my database?

I'm trying to insert the title from my input form into my caption column in my database and then display it with the post. The image and delete buttons display, however the title does not.
Here is my route in my server.js:
app.post('/newPost', isLoggedIn, uploads.single('inputFile'), (req, res) => {
console.log('On POST route');
// get an input from user
let file = req.file.path;
console.log(file);
cloudinary.uploader.upload(file, (result) => {
console.log(result);
db.post.create({
caption: req.body.title,
image_url: result.url,
userId: req.body.id
})
// Render result page with image
}).then((post) => res.render('index', { image: result.url }));
})
Here is my newPost.ejs which contains the form:
<div class="postSection">
<form action="/" method="POST" enctype="multipart/form-data">
<input type="title" placeholder="Write a Caption:" id="postText">
<input type="file" name="inputFile" id="inputFile">
<input type="submit" class="btn btn-primary" id="postSubmit" value="Post">
</form>
</div>
And, finally here is my index.ejs page in which it will display:
<div>
<% posts.forEach(function(post) { %>
<h1><%= post.caption %></h1>
<img class="images" width="700px" height="500px" src="<%= post.image_url %>" alt="uploaded image">
<form action="/<%= post.id %>?_method=DELETE" method="POST">
<input id="deleteButton" class="btn-danger" type="submit" value="Remove idea" >
</form>
<br>
<% }) %>
</div>
Can anyone spot why the title isn't being inserted into my database and also why it isn't displaying?
One option to debug is console.log the req.body and search for the text you sent as the title.
I think that the title is in req.body.postText or you should add a name tag to your title input and that will be the name in your req.body.
Let me know if this helps!

Cannot POST form NodeJS Express

I have a form which dynamically adds user data from the user into a table.
This is the final form look
When one of these Use this account is pressed it shows, Cannot POST /like.
This is the form. I useEJS.
<form method="POST" autocomplete="off">
<div class = "form-group col-sm-12">
<label for="tagsarray">Enter Tags</label>
<input type="text" class="form-control" id="likes" name="likes" aria-placeholder="Enter tags seperated by a comma">
</div>
<div class="form-group col-sm-7">
<label for="numberoftimes">Enter Number of Actions</label>
<input type="text" class="form-control" id="action" name="action" aria-placeholder="Enter No. of Actions">
</div>
<% if (accounts) {%>
<table class = "striped">
<tbody>
<% accounts.forEach(accounts => { %>
<tr>
<td><%= accounts.title%></td>
<td><button type="submit" class = "btn btn-primary col-auto" >Use this account</button></td>
</tr>
</tr>
<%});%>
</tbody>
</table>
<%} else {%>
<p>You have no accounts added</p>
<% } %>
</form>
This is my controller.js
control.get('/like', async(req, res) => {
try{
const accounts = await account.find({user: req.user.id}).lean()
res.render("backend/like", {
name: req.user.name,
accounts
});
} catch(err) {
console.log(err)
}
});
control.post('/like/:id', getuserdata, (req, res, next) => {
try{
let title = res.accountuser.title;
let pass = res.accountuser.password;
let tags = req.body.likes;
let actions = req.body.action;
console.log(title, pass, tags, actions)
iglike(title, pass, tags, actions)
next();
res.redirect('/like')
}catch(err) {
console.log(err)
}
});
This does not catch any error and the console shows absolutely nothing. The only error is Cannot POST /like.
This is the getuserdata function for reference
async function getuserdata(req, res, next) {
let accountuser
try{
accountuser = await account.findById(req.params.id)
} catch(err) {
console.log(err)
}
res.accountuser = accountuser
next()
};
I have tried with a simple type=submit button with no href still shows the same error
Please help me solve this Cannot POST /like error.
You have to set a default action for your form, to make the route to work with post. If you use href it does a get by default.
To make a post in your route, you should remove the href and put an default action in your form.
<form method="POST" autocomplete="off" action="/like/<%= accounts._id%>">
Then in the line your have the submit button, remove the href (as it was moved to default action from form)
<td><button type="submit" class = "btn btn-primary col-auto" >Use this account</button></a></td>
/like is a GET request
for POST request you need to use
/like/1
where 1 can be any parameter

Not able to compare Object Id using handlebars?

This is my first time using Handlebars. I am trying to compare user _id with logged in user id. I am saving data in MongoDB using mongoose. I found a helper from StackOverflow but it doesn't seem to compare value. The schema for user and Tracker and code I am using is below.
var trackerSchema = new mongoose.Schema({
description: {
type: String,
required: [true, 'Please add an description']
},
createdAt: {
type: Date,
default: Date.now
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
});
mongoose.model('Tracker', trackerSchema);
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
const User = mongoose.model('User', UserSchema);
Helper function in app.js file
const hbs = exphb.create({
extname: 'hbs',
defaultLayout: 'mainLayout',
layoutDir: __dirname + 'views/layouts/',
partialsDir: __dirname + '/views/partials/',
helpers: {
ifEqual: function (v1, v2, options) {
if (v1 != v2) {
return options.inverse(this);
}
return options.fn(this);
},
}
})
Using helper function ifEqual in handlebar file
{{#each list}}
<td>{{this.createdAt}}</td>
<td>{{this.user}}</td> //i am seeing this.user for example - **5ef9fb54f4bb8dff810104f7**
<td>
{{#ifEqual this.user looged_user_id }} //looged_user_id coming from req.user._id.toString()
Edit
<a href="/tracker/delete/{{this._id}}" onclick="return confirm('Are you sure to delete this record ?');"> Delete
</a>
{{else}}
Edit
<a href="#" onclick="return confirm('Are you sure to delete this record ?');"> Delete
</a>
{{/ifEqual}}
</td>
</tr>
{{/each}}
Please guide how can I compare two _ids.
You were missing two things:
Sending looged_user_id to your hbs:
res.render("tracker/list", { list: body, logged_user_id: req.user._id, });
Accessing logged_user_id it the right way inside the #each handlebar loop. When inside the loop you cannot directly use the data passed. You have to provide the right path to logged_user_id which in your case would be ../logged_user_id.
The ../ path segment references the parent template scope using which you can access logged_user_id.
In your code you need to pass your looged_user_id to template first, then only it will get in your helper function.
try this on http://tryhandlebarsjs.com/
Template:
{{#each list}}
<td>{{this.createdAt}}</td>
<td>{{this.user}}</td>
<td>
{{#ifCond this.user}} //Here 1 is just hardcoded insted of that need to change with actal variable
Edit for if
<a href="/tracker/delete/{{this._id}}" onclick="return confirm('Are you sure to delete this record ?');"> Delete looged_user_id
</a>
{{else}}
Edit for else
<a href="#" onclick="return confirm('Are you sure to delete this record ?');"> Delete
</a>
{{/ifCond}}
</td>
</tr>
{{/each}}
Context:
{
"list": [{
"user": 1,
"content": "hello"
},{
"user": 2,
"content": "world"
}],
}
Register Helper function:
Handlebars.registerHelper('ifCond', function(v1, options) {
if(v1 === looged_user_id ) {
return options.fn(this);
}
return options.inverse(this);
});
Result:
<td></td>
<td>1</td>
<td>
Edit for if
<a href="/tracker/delete/" onclick="return confirm('Are you sure to delete this record ?');"> Delete looged_user_id
</a>
</td>
</tr>
<td></td>
<td>2</td>
<td>
Edit for else
<a href="#" onclick="return confirm('Are you sure to delete this record ?');"> Delete
</a>
</td>
</tr>

ReferenceError: product is not defined

I'm trying to fetch the data , but when i console it.I' getting the data but in rendering it's not working in front end its showing product is not defined.
app.get('/profile/profile/dashboard/update/:id',function (req,res,next) {
//console.log("hello "+req.params.id);
Product.findOne({_id: req.params.id}).then(product=>{
console.log("hello check please "+ product);
});
res.render('update.ejs',{ product: product });
});
update.ejs file
<% if (product._id) { %>
<tbody>
<td><input type="text" value="<%= product.name %>" required></td>
<td><input type="text" value="" required></td>
<td><input type="text" value="" required></td>
<td><input type="text" value="" required></td>
<td><input type="submit" class="btn btn-danger" value="submit"></td>
</tbody>
<% } %>
The problem is that you are trying to render the data before the findOne is finished. findOne is asynchronic function and that is why the log works for you.
Move res.render inside the then scope:
Product.findOne({_id: req.params.id}).then(product=>{
console.log("hello check please "+ product);
res.render('update.ejs',{ product: product });
});
Yes problem with your code is rendering is placed outside of the find one method. Put it inside the braces like.
app.get('/profile/profile/dashboard/update/:id',function (req,res,next) {
//console.log("hello "+req.params.id);
Product.findOne({_id: req.params.id}).then(product=>{
console.log("hello india "+ product);
res.render('update.ejs',{ product: product });
});
});

Clear Search on Semantic-ui

How do I get the selected result of a search field of Semantic-UI?
Using onSelect event, I can get the selected result, but this does not work when clean the field with the backspace.
Using field.search('get result') also not return the result blank as expected.
You can get the selected result like this:
onSelect:function(result){
console.log(result)
}
Having a sample HTML form with 2 inputs like this (one hidden):
<form id="search-box-form">
<input type="hidden" name="search" value="" />
<div class="ui search location-search">
<div class="ui icon input">
<input class="prompt" type="text" placeholder="Search locations..." />
<i class="search icon"></i>
</div>
<div class="results"></div>
</div>
</form>
The same way you use onSelect to populate your hidden field, you can use onResultsClose to clear your hidden input. Caveat: onResultsClose will run before semantic puts the value on it's own field, hence the need to use timeout function.
jQuery(document).ready(
function() {
jQuery('.location-search')
.search({
source : content,
searchOnFocus : true,
minCharacters : 0,
onSelect: function(result, response) {
var value = result.value;
$('#search-box-form').find('input[name=search]').val(value);
},
onResultsClose: function(){
setTimeout(function() {
if (jQuery('.location-search').search('get value') == '') {
$('#search-box-form').find('input[name=search]').val('');
}
}, 500);
}
})
;
}
);

Resources