Unable to fetch array into an EJS file - node.js

I want to display an appropriate error message that I am pushing in an array 'errorsList' and sending it to ideas/add.ejs as the context. In the EJS, i am iterating over errorsList and fetching the elements. The webpage displays an error saying errrorsList is not defined.
I have tried to pass the JSON directly instead of putting it into some variable just to check, still in that case it gives me an error as errorsList is not defined
App.js
// Process form
let errorsList = [];
if(!request.body.title) {
errorsList.push({ text: 'Please add a title' });
}
if(!request.body.details) {
errorsList.push({ text: 'Please add something' });
}
console.log('Errors List: ', errorsList);
if(errorsList.length > 0) {
const context = {
errorsList: errorsList,
title: request.body.title,
details: request.body.detail,
pageTitle: 'Ideas'
}
response.render('ideas/add', context);
}
else {
const context = {
pageTitle: 'Ideas'
};
response.render('ideas/ideas', context)
}
});```
add.ejs
<div class="container">
<% errorsList.forEach((error) => { %>
<div class="alert alert-danger">
<%= error.text %>
</div>
<% }) %>
Here i get the error as errorsList is not defined

Related

Updating array of objects in mongoose based off key in objects value

I have two ejs forms that when hit, make an HTTP post request to my /api/users/makePicks/:id route. This route hits my controller which updates the Users model in my mongodb with the NFL picks they submitted in the EJS form.
I need this route to create the picks object for each route if they do not exist for that particular week, and if they do exist it needs to update the picks that are already there. The picks are being stored in my User model in an array, this array contains objects for each weeks picks. Currently the code, with much help from Mohammed, is successfully pushing code to to array. But i cannot seem to figure out how to update the picks if an object with a key of that week exists.
My validation is finally working properly. What I mean is we are running a for loop on the picks array, it will console.log true if there is already a matching picks object with for that weeks picks, if the object with a first key value with the current weeks form doesn't exist, it will console.log false and push the new picks to the array.
The only part that isn't working is the if statement nested within my for loop, it is not updating the object if it already exists in the picks.array. But as I said, the validation is working correctly. I suspect the line of code
result.picks[i] = { [`week-${req.params.week}`]:req.body };
is for some reason not updating object with the updated req.body.
Controller
exports.makePicks = async (req, res, next) => {
const picks = req.body;
try {
let result = await User.findById(req.user._id);
if (result.picks.length > 0) {
for (let i = 0; i < result.picks.length; i++) {
if ((Object.keys(result.picks[i])[0] == [`week-${req.params.week}`])) {
console.log(chalk.green("true"));
result.picks[i] = { [`week-${req.params.week}`]:req.body };
break;
} else {
console.log(chalk.red("false"));
result.picks.push({ [`week-${req.params.week}`]: picks });
break;
}
}
} else {
result.picks.push({ [`week-${req.params.week}`]: picks });
console.log(chalk.yellow('results.picks is empty'))
}
await result.save();
res.redirect("/api/dashboard");
} catch (error) {
console.log(error);
}
};
result.picks example structure
[{"week-1":
{"jojo-0":"ARI","jojo-1":"ARI","jojo-2":"ARI"}
},
{"week-2":
{"jojo-0":"ATL","jojo-1":"ATL","jojo-2":"BAL"}
},
{"week-3":
{"jojo-0":"ARI","jojo-1":"ARI","jojo-2":"ARI"}
}]
Router
router.route('/makePicks/:week')
.post(controller.makePicks);
EJS
<% const teamsArr = ['ARI', 'ATL', 'BAL', 'BUF', 'CAR', 'CHI', 'CIN', 'CLE', 'DAL', 'DEN', 'DET', 'GB', 'HOU', 'IND', 'JAX',
'KC', 'LAC', 'LAR', 'LV', 'MIA', 'MIN', 'NE', 'NO', 'NYG','NYJ', 'PHI', 'PIT', 'SEA', 'SF', 'TB', 'TEN', 'WAS' ] %>
<form class="mt-3 mb-3" method="POST" action="/api/users/makePicks/1">
<% for(i=0; i < user.bullets; i++){ %>
<div class="form-group">
<label for="<%= `${user.name}-${i}` %>">Make your pick for bullet <%= `${i}` %></label>
<select class="form-control" name="<%= `${user.name}-${i}` %>" id="<%= `${user.name}-${i}` %>">
<% teamsArr.forEach(team => { %>
<option value="<%= team %>"><%= team %></option>
<% }) %>
</select>
</div>
<% }; %>
<button type="submit" class="btn btn-primary">Save changes</button>
</form>
<form class="mt-3 mb-3" method="POST" action="/api/users/makePicks/2">
<% for(i=0; i < user.bullets; i++){ %>
<div class="form-group">
<label for="<%= `${user.name}-${i}` %>">Make your pick for bullet <%= `${i}` %></label>
<select class="form-control" name="<%= `${user.name}-${i}` %>" id="<%= `${user.name}-${i}` %>">
<% teamsArr.forEach(team => { %>
<option value="<%= team %>"><%= team %></option>
<% }) %>
</select>
</div>
<% }; %>
<button type="submit" class="btn btn-primary">Save changes</button>
</form>
you want to using $push and $set in one findByIdAndUpdate, that's impossible, I prefer use findById() and process and save() so just try
exports.makePicks = async (req, res, next) => {
const picks = req.body;
try {
//implementation business logic
let result = await User.findById(req.user._id)
if(result.picks && result.picks.length > 0){
result.picks.forEach(item =>{
if([`week-${req.params.week}`] in item){
item[`week-${req.params.week}`] = picks
}
else{
result.picks.push({ [`week-${req.params.week}`] : picks })
}
})
}
else{
result.picks.push({ [`week-${req.params.week}`] : picks })
}
await result.save()
res.redirect('/api/dashboard');
} catch (error) {
console.log(error)
}
}
note: don't use callback and async/await together
exports.makePicks = async (req, res, next) => {
const picks = req.body;
const { week } = req.params;
try {
let result = await User.findById(req.user._id);
const data = { [`week-${week}`]: picks };
const allPicks = [...result.picks];
if (allPicks.length > 0) {
// Search index of peek
const pickIndex = _.findIndex(allPicks, (pick) => {
return Object.keys(pick)[0] == `week-${week}`;
});
// If found, update it
if (pickIndex !== -1) {
console.log(chalk.green("true"));
allPicks[pickIndex] = data;
}
// Otherwise, push new pick
else {
console.log(chalk.red("false"));
allPicks.push(data);
}
} else {
allPicks.push(data);
console.log(chalk.yellow('results.picks is empty'))
}
result.picks = allPicks;
console.log('allPicks', allPicks);
await result.save();
res.redirect("/api/dashboard");
} catch (error) {
console.log(error);
}
};

AJAX call is broken, NodeJS, Express, Handlebars

I've been working on this for a couple of days. I'm certain its something really stupid, but I'm at the end of my sanity.
The public files are set up properly.
Error Message:
Uncaught ReferenceError: togDefine is not defined
Front End HTML:
<li class="list-group-item list-group-item-dark">
<div class="row">
<div class="col-md-4"><strong>Heating:</strong> {{#if heating}} {{this.heating}} {{else}} N/A {{/if}}</div>
<div class="col-md-4"><strong>Cooling:</strong> {{#if cooling}} {{this.cooling}} {{else}} N/A {{/if}}</div>
<div class="col-md-4">
<input type="checkbox" id="pvt{{this.id}}" checked="{{this.private}}" onchange="togDefine({{this.id}}, {{this.private}});" data-toggle="toggle" data-on="Private" data-off="Public" data-onstyle="success" data-offstyle="danger" />
</div>
</div>
AJAX Call:
$(function() {
// Private-Public toggle
let togDefine = (id, pvt) => {
$.ajax({
type: "POST",
url: "/api/pvtToggle",
data: {
id: id,
newState: (pvt === 'true') ? false : true
},
success: function(text) {
if (text === 'ok') {
pvtSuccess(id, pvt);
} else {
console.log('updatePvt failed');
}
}
});
};
let pvtSuccess = (id, pvt) => {
$('#pvt' + id).attr('checked', (pvt === 'true') ? 'false' : 'true');
};
});
Back End:
//TOGGLE Private vs Public PROPERTY
app.put('/api/pvtToggle/', isAuthenticated, function(request, response) {
db.Prop.update({
private: request.params.newState
}, {
where: {
id: request.params.id
}
}).then(data => {
response.send('ok');
}).catch(error => {
console.log(error);
});
});
Please help me figure out why the request isn't working properly. :D
Your function togDefine() is defined inside this block:
$(function() { /* in here */ })
Therefore, that function name is only available inside that block and is not available to your HTML. Since just defining a function doesn't actually execute anything, there is really no reason to define a function inside of that kind of block unless you ONLY want the symbol available inside that block.
Since you explicitly don't want that here, just move the definition of togDefine() outside that block.

Object not found, using parse nodejs

I'm new using parse and I'm trying to get the objects from my database and displaying them with ejs using a for loop in my webpage. I'm using back4app as my database.
Here's what I'm doing:
const Car = Parse.Object.extend('Vehicle');
const query = new Parse.Query(Car);
app.get('/', function(req, res){
const VehicleInfo = [
{
VehicleName: query.get('Name'),
Description: query.get('Description'),
Price: query.get('Price'),
Rating: query.get('Rating'),
Route: query.get('Route'),
PassengerAmount: query.get('PassengerAmount')
}
]
try{
res.render('index', {
title: 'mainPage',
VehicleData: VehicleInfo
});
}catch(error){
throw error.message;
}
});
I query this and all 5 of my vehicles are displayed in the console.log but when trying to do the same in my .ejs file this shows up and only one div displays
enter image description here
Here's how I'm using the for loop
<% for (var CarInfo of VehicleData) { %>
<div class="row">
<div class="col-lg-4 col-md-6">
<!-- Car Item-->
<div class="rn-car-item">
<div class="rn-car-item-review">
<div class="fas fa-star"></div> <%= CarInfo.Rating %>
</div>
<div class="rn-car-item-thumb">
<a href="/car-single">
<img class="img-fluid" src="/images/car-1.jpg" alt="Black Sedan" srcset="/images/car-1.jpg 1x, /images/car-1#2x.jpg 2x"/>
</a>
</div>
<div class="rn-car-item-info">
<h3>
<%= CarInfo.VehicleName %>
</h3>
<p>Descripcion: <%= CarInfo.Description %></p>
<div class="rn-car-list-n-price">
<ul>
<li>Ruta: <%= CarInfo.Route %></li>
<li>Cantidad de Pasajeros: <%= CarInfo.PassengerAmount %></li>
</ul>
<div class="rn-car-price-wrap">
<a class="rn-car-price" href="/car-single">
<span class="rn-car-price-from">Desde</span>
<span class="rn-car-price-format">
<span class="rn-car-price-amount">$<%= CarInfo.Price %></span>
<span class="rn-car-price-per">/day</span>
</span>
</a>
</div>
</div>
</div>
</div>
<!-- End Car Item-->
</div>
</div>
<% } %>
I'm sure your code doesn't work like this, also not in the console. You need to run find or first in order to fetch objects.
The other problem is that your Promise hasn't been resolved and doesn't contain the result when you pass it on to the .ejs file. It works in the console because the result in the console will be updated once the Promise is resolved.
You need to do
const VehicleInfo = [];
const query = new Parse.Query(Car);
query.find().then(result => {
result.forEach(vehicle => {
VehicleInfo.push({
VehicleName: result.get('Name'),
Description: result.get('Description'),
Price: result.get('Price'),
Rating: result.get('Rating'),
Route: result.get('Route'),
PassengerAmount: query.get('PassengerAmount')
});
});
}).catch(error => {
console.error('error fetching objects', error);
});
Alternatively you can await the result for cleaner code:
app.get('/', async function(req, res) {
const VehicleInfo = [];
const query = new Parse.Query(Car);
try {
const result = await query.find();
result.forEach(vehicle => {
VehicleInfo.push({
VehicleName: result.get('Name'),
Description: result.get('Description'),
Price: result.get('Price'),
Rating: result.get('Rating'),
Route: result.get('Route'),
PassengerAmount: query.get('PassengerAmount')
});
});
} catch (error) {
console.error('error fetching objects', error);
}
});
Here's more about Promises in JavaScript

How do I have a variable available to display on my success page, after adding items to a database via a /POST route?

I would like to display the doc.id variable of a successful /POST of data to a route, on the success page that the user will be redirected to afterward. I'm trying to work out how to carry the variable teamId through to the Handlebar template page success.hbs
I've tried making it a variable, and setting up a Handlebar helper to display it, but nothing is working.
/POST route redirecting to success.hbs:
app.post('/create', (req, res) => {
var players = [];
var playerObj = {};
for (let i = 1; i < 21; i++) {
var playerObj = { playerName: req.body[`player${i}Name`], playerNumber: req.body[`player${i}Number`], playerPosition: req.body[`player${i}Position`] };
if (req.body["player" + i + "Name"] === '') {
console.log("Empty player name detected, disregarding");
} else {
players.push(playerObj);
}
}
var newTeam = new Team({
// WEB SETUP BELOW
"team.teamRoster.teamCoach": req.body.coachName,
"team.shortTeamName": req.body.teamShortName,
"team.teamName": req.body.teamName,
"team.teamRoster.players": players
});
newTeam.save().then((doc) => {
var teamId = doc.id;
console.log(teamId);
res.render('success.hbs');
console.log("Team Added");
}, (e) => {
res.status(400).send(e);
});
});
/views/success.hbs
<div class="container-fluid" id="body">
<div class="container" id="page-header">
<h1><span id="headline">Team Added Succesfully</span></h1>
<hr>
<h3><span id="subheadline">Input the following address as a JSON Data Source within vMix.</span></h3>
<span id="content">
<div class="row">
<div class="container col-md-12">
{{{teamId}}}
</div>
</div>
</span>
</div>
<hr>
</div>
I'd like a Handlebar helper to get the doc.id value of the /POST request, and store it as teamId to display on the success page. It's finding nothing at the moment.
Any help is appreciated.
Node.js can pass variables to the handlebars-view like this:
newTeam.save().then((doc) => {
var teamId = doc.id;
console.log(teamId);
res.render('success.hbs', {
teamId
});
console.log("Team Added");
}, (e) => {
res.status(400).send(e);
});

Generating an array in the view and passing it into the controller

It's a little strange but I cannot think of a better way to get it done.
First of all this is my code:
The router to get the view in the first place
router.get('/', function(req, res, next) {
Account.findOne(
{
_id: req.user._id,
},
function(err, acc) {
if (err) {
console.log(err);
}
// console.log(acc.websites);
res.render('reports/index', {
title: 'Reports!',
websites: acc.websites,
user: req.user,
});
}
);
});
The view:
<% include ./../partials/header.ejs %>
<h1 class="text-center">This is your report page</h1>
<form method="post">
<% for(let i=0; i<websites.length; i++){ let website = websites[i]; %>
<fieldset>
<label for="website<%=i%>" class="col-sm-2">Website <%=i+1%></label>
<input name="website<%=i%>" id="website<%=i%>" value="<%=website%>" type="text" />
</fieldset>
<% } %>
Generate report
</form>
<% include ./../partials/footer.ejs %>
The router, that's supposed to fire up after the on click.
router.get('/reports', function(req, res, next) {
if (req.user.isPremium == false) {
// Free user - Single report
var builtWithCall = `https://api.builtwith.com/free1/api.json?KEY=00000000-0000-0000-0000-000000000000&LOOKUP=${website}`;
let website = req.body.website0;
console.log(website);
}
});
How it works: The controller finds the account, grabs an array from inside of it, sends it to the view. The view prints it out and allows to make some changes to the values.
And now is where the problems begin. I need to gather the new values into an array and send it to the next router, which will then use them to call a bunch of APIs and print out the data. How do I gather the array and pass it to the controller? Also, should I use GET or POST?
With your logic, the name attribute will have the following form: name=website[0...n]. With that in mind, we can filter out the keys to gather all the website[n] into an array you seek:
const example = {
website0: 'example',
website1: 'example',
website2: 'example',
website3: 'example',
shouldBeIgnored: 'ignoreMe',
ignore: 'shouldIgnore'
}
const websites = Object.keys(example).filter(key => key.startsWith('website'))
console.log(websites)
So you're controller can be:
router.get('/reports', (req, res, next) => {
if (!req.user.isPremium) {
// Free user - Single report
const builtWithCall = `https://api.builtwith.com/free1/api.json?KEY=00000000-0000-0000-0000-000000000000&LOOKUP=${website}`;
const websites = Object.keys(req.body).filter(key => key.startsWith('website'));
console.log(websites);
}
});

Resources