How to handle the checkbox state with node and mongodb - javascript

I have an ejs file, which populates my checkboxes, like below. I do use an if statement to set the checkbox either as checked or unchecked...
<% taskList.forEach((task) => { %>
<div class="task">
<form class="task-content" action="/checked-box" method="post">
<% if (task.solvedOn === false) { %>
<input type="checkbox" name="checkbox" value="<%= task._id %>" onchange="this.form.submit()" checked="<%= task.solvedOn %>">
<% } else { %>
<input type="checkbox" name="checkbox" value="<%= task._id %>" onchange="this.form.submit()" checked="<%= task.solvedOn %>">
<% } %>
<p> <%= task.name %> </p>
<input type="hidden" name="taskListID" value="<%= list._id %>"></input>
<input type="hidden" name="taskListTitle" value="<%= list.title %>"></input>
</form>
</div>
<% }); %>
Now, whenever some clicks on the checkbox, node runs the following code to save the checkbox-state into a mongo DB.
app.post('/checked-box', (req, res) => {
const taskID = req.body.checkbox;
const listTitle = req.body.taskListTitle;
const listID = req.body.taskListID;
List.findOne(
{_id: listID},
{tasks: {
$elemMatch: {
_id: taskID
}
}},
(err, result) => {
console.log(result.tasks);
if (result) {
const checkboxStatus = result.tasks[0].solvedOn;
console.log(checkboxStatus);
if (checkboxStatus === false) {
List.updateOne(
{_id: listID, "tasks._id": taskID},
{$set: {
"tasks.$.solvedOn": true
}},
(err) => {
if (!err) {
res.redirect('/' + listTitle)
}
}
)
} else {
List.updateOne(
{_id: listID, "tasks._id": taskID},
{$set: {
"tasks.$.solvedOn": false
}},
(err) => {
if (!err) {
res.redirect('/' + listTitle)
}
}
)
}
} else {
console.log(err);
}
}
)
})
The problem is, that whenever I click on a checked Checkbox the task runs into an error:
undefined
node:events:504
throw er; // Unhandled 'error' event
^
TypeError: Cannot read properties of undefined (reading '0')
But, if it is unchecked it works totally fine.
Does anyone know why? or how can I solve that problem?

Related

How to add logout button if user isn't logged in with Nodejs

I am learning Web development by making projects. I am trying to display Login and Logout button based on the login status of the user on my website. I am using ExpressJs, PassportJs and EJS. I tried this but I am getting error.
EJS code:
<form class="form-inline mt-2 mt-md-0">
<% if (login_info) { %>
Logout
<% } %>
<% else { %>
Login
<% } %>
</form>
Server Side Code:
router.get('/dashboard', function (req,res){
if (!req.user) {
res.render('dashboard', { login_info: 'false' });
}
else {
res.render('dashboard', { login_info: 'true' });
}
});
Error
SyntaxError: Unexpected token 'else' in C:\Users\nodeapp\views\dashboard.ejs while compiling ejs
Is there anything I am doing wrong here?
Thanks
You are initializing value of login_info as string not as boolean. Can you check if it works
router.get('/dashboard', function (req,res){
if (!req.user) {
res.render('dashboard', { login_info:false });
}
else {
res.render('dashboard', { login_info:true });
}
});
And on your ejs keep } else on same line as
<% if (true) { %>
<p> valid!!!</p>
<%} else { %>
<p> Not Allowed </p>
<% } %>

EJS function parameter is not defined

I have 50 different image inputs and I dont want to write them all, so
I want to use a function where I can set the parameters and only have to call the func with the parameters. Inside the ejs (html) I want to set the name attribut like this name="<% name2 %>" but if I call the func with value painting3 for name it throws
58| <% callPainting(post.paintings, paintings3, ) %>
paintings3 is not defined
I defined post in app.js
router.get('/:id/edit', async (req, res) => {
try {
const findPost = await Post.findById(req.params.id)
res.render('admin/edit', {
layout: 'layouts/admin',
post: findPost,
})
} catch {
res.redirect('/admin')
}
})
and every input should looks like this <input type="file" class="filepond" name="paintings2" value="<%= post.paintings2 %>">
So i did this, but its not working as I expected
<% function callPainting(postimage, postname, postdate, postprice, checkImage, postshowImage, name, name2, date, price, showImage) { %>
<input type="file" class="filepond" name="<% name %>" value="<%= postimage %>">
<% } %>
I got some more parameters but thats the same problem

How do I render a handlebars layout in node.js from a button?

This is a really weird issue I am having.
I have a login form, this login form verifies your data and renders the Profile layout if the login is successful OR renders the register page if the login is not.
exports.logIn = function (req, res, data) {
var username = req.body.username.toString();
var password = req.body.password.toString();
connection.connection();
global.connection.query('SELECT * FROM Utilizador WHERE Nome_Utilizador = ? LIMIT 1', [username], function (err, result) {
if (result.length > 0) {
if (result) {
var object = JSON.parse(JSON.stringify(result));
var userObject = object[0];
var userQ = object[0].Nome_Utilizador;
global.connection.query('SELECT Password_Utilizador from Utilizador where Nome_Utilizador = ?', [username], function (err, result) {
console.log(result);
if (result.length > 0) {
if (result) {
var object2 = JSON.parse(JSON.stringify(result));
var passQ = object[0].Password_Utilizador;
if (password == passQ) {
console.log("Login efectuado com sucesso");
console.log(userObject);
res.render('home', { title: 'perfil', layout: 'perfil', data: userObject });
} else {
console.log("1");
}
}
} else if (err) {
console.log("asdsadas");
} else {
console.log("2");
res.render('home', { title: 'perfil', layout: 'registo' });
}
});
}
} else if (err) {
console.log(err);
} else {
console.log("Utilizador nao encontrado");
res.render('home', { title: 'perfil', layout: 'registo' });
}
});
};
This works.
And the only reason why it does work is because it comes from a FORM with a METHOD and an ACTION
<form id="login-nav" action="/login" method='POST' role="form" accept-charset="UTF-8" class="form">
<div class="form-group">
<label for="username" class="sr-only">Utilizador</label>
<input id="username" type="username" placeholder="Nome de utilizador" required="" class="form-control" name="username">
</div>
<div class="form-group">
<label for="exampleInputPassword2" class="sr-only">Palavra-Passe</label>
<input id="password" type="password" placeholder="Meta a palavra-passe" required="" class="form-control" name="password">
</div>
<div class="checkbox">
<label></label>
<input type="checkbox">Gravar Dados
</div>
<div class="form-group">
<button id="botaoLogin" class="btn btn-danger btn-block">Fazer Login</button>
</div>
</form>
However, I tried to do the same thing with jQuery, as I need to render a Handlebars layout for some products on button click,
$("#pacotes").on('click', ".produto", function () {
var prod = this.id;
console.log(prod);
$.get("http://localhost:3000/pacote?idPacote=" + prod);
});
And despite the query working and giving me the data I requested
exports.Pacote = function (req, res) {
var pacote = req.query.idPacote;
connection.connection();
global.connection.query('SELECT * FROM Pacotes WHERE idPacotes = ? ', [pacote], function (err, result) {
if (result.length > 0) {
if (result) {
var object = JSON.parse(JSON.stringify(result));
var packObject = object[0];
console.log(result);
res.render('home', { title: 'pacote', layout: 'pacote', data: packObject });
} else if (err) {
console.log(err);
}
};
});
}
It simply doesn't render the layout and I have no idea why.
What is the difference between doing a POST request like this or doing it by a form?
I don't understand why this only seems to work with forms.
I could solve it that way, but I don't think using empty forms for all my buttons would be a viable solution.
You are only making a request, you are not processing the return value:
$.get("http://localhost:3000/pacote?idPacote=" + prod);
Try changing to something like:
$.ajax({
method: 'GET',
url: "http://localhost:3000/pacote?idPacote=" + prod,
success: function(...) {...}
});

Flash message doesn't show until refresh or secondary post?

I am jumping into Sails.js again and working through to create flash messages throughout my app (for errors, successes, or alerts). I was looking for a good way to do it and found this discussion, I implemented the solution they had suggested.
The general mechanism works great, however, the flash message is only seen after a secondary refresh or after another post. It does not show at first upon page load. Here is how I have everything structured and I am using "sails": "~0.10.0-rc7" currently:
In my api/policies folder, I have flash.js:
// flash.js policy
module.exports = function(req, res, next) {
res.locals.messages = {
success: [],
error: [],
warning: []
};
if(!req.session.messages) {
req.session.messages = { success: [], error: [], warning: [] };
return next();
}
res.locals.messages = _.clone(req.session.messages);
// Clear flash
req.session.messages = { success: [], error: [], warning: [] };
return next();
};
In my api/services, I have FlashService.js:
// FlashService.js
module.exports = {
success: function(req, message) {
req.session.messages['success'].push(message);
},
warning: function(req, message) {
req.session.messages['warning'].push(message);
},
error: function(req, message) {
req.session.messages['error'].push(message);
}
}
My config/policies.js is also configured with the flash policy:
// config/policies.js
module.exports.policies = {
'*': [true, 'flash'],
'UserController': {
'join': ['flash'],
},
};
Now, with all that setup, an example of how I am using it is in my UserController for my join action:
module.exports = {
join: function(req, res) {
// If username, email, and password compare come back true: create user.
if(newUser.username && newUser.email && newUser.password == newUser.confirmPassword) {
// Logic to create user.
res.view();
// If not, then report an issue.
} else {
FlashService.error(req, 'There was an issue.');
res.view();
};
}
};
Finally, my view is exactly the same code as that discussion I linked. I am using EJS on this:
<% if (messages && messages['error'].length > 0) { %>
<div class="alert alert-danger">
<% messages['error'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['warning'].length > 0) { %>
<div class="alert alert-warning">
<% messages['warning'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
<% if (messages && messages['success'].length > 0) { %>
<div class="alert alert-success">
<% messages['success'].forEach(function(message) { %>
<%= message %>
<br>
<% }); %>
</div>
<br>
<% } %>
What could I be doing wrong? Any help would be much appreciated!
Thanks,
Your messages are being read before the the controller is executed (and just message is set after it is read). The req object is available in your view, you should just read req.session.messages['xxxx'] directly into your view.

nodejs/express/mongo Have res.render wait until after database search is complete to load

I'm having some trouble getting this table to load properly because the page is loading before all the information is passed to my ejs template. Pretty new to all of this and would appreciate any help!
I should note that owneditems is an array of IDs in the user schema.
routes.js:
app.get('/profile/:username', function(req, res) {
User.findOne({username: req.params.username}, function(err, user) {
var newDocs = [];
if (!user) {
req.flash('profilemessage', 'No such user exists.');
} else {
user.owneditems.map(function(i) {
Items.findById(mongoose.Types.ObjectId(i), function(err, idoc) {
newDocs.push("<tr><td>" + idoc.name + "</td><td>" + idoc.brand</td></tr>");
});
});
}
res.render('profile.ejs', {title: 'Profile', items: newDocs, message: req.flash('profilemessage')});
});
});
Profile.ejs:
<!-- content -->
<div class="wrapper row2">
<div id="container" class="clear">
<section>
<% if (message) { %>
<h4><%= message %></h4>
<% } %>
<table id="owneditems" class="sortable">
<tr><th>Name</th><th>Brand</th></tr>
<% for(var i=0; i<items.length; i++) {%>
<%- items[i] %>
<% } %>
</table>
</section>
</div>
</div>
<% include layoutBottom %>
This type of setup works for me on another page, I just can't get it working here. Thanks!
The reason why the page is rendered before information is loaded is becauseItems.findById is asynchronous. This means newDocs will not return the array of items you're expecting when it's passed to res.render.
When you want to load (arrays of) subdocuments with Mongoose, it's best to use query#populate. This method will allow you to swap out the item IDs in your user.owneditems array for the actual item document in one go.
I think this would work in your case:
app.get('/profile/:username', function(req, res) {
User.findOne({username: req.params.username})
.populate('owneditems')
.exec(function(err, user) {
var newDocs = [];
if (!user) {
req.flash('profilemessage', 'No such user exists.');
} else {
user.owneditems.forEach(function(i) {
newDocs.push("<tr><td>" + i.name + "</td><td>" + i.brand</td></tr>");
});
}
res.render('profile.ejs', {title: 'Profile', items: newDocs, message: req.flash('profilemessage')});
});
});
Also note I switched map with forEach (which is what it seems you're going for given your callback)

Categories

Resources