EJS not displaying value if only single item passed - html

well while passing value from app.js file to checkout.ejs file, if I am checking multiple checkboxes, all the images associated gets displayed in checkout. ejs page, but if I press only a single checkbox to retrieve single image, it doesn't happen.
here is my app.js
app.post("/data", uploads, function (req, res) {
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
res.render("checkout",{SIMG:req.body.checkbox});
}
}
});
});
and here is my checkout.ejs
<% for(var i=0; i<SIMG.length; i++){ %>
<p> <img src="./uploads/<%=SIMG[i]%>" alt="image" width="300"></p>
<% } %>
</body>
</html>
In app.js, req.body.checkbox retrieves the image name of all the images selected from other pages, that I want to retrieve in the checkout page.
working fine if selected images are more than one, but for a single image
<img src="./uploads/<%=SIMG[i]%>" alt="image" width="300">
line in checkout.ejs runs continuously without displaying an image.

I guess you are getting array when you selecting multiple checkboxes but when you select one checkbox it will just give value, that's why it is not working. If you get single value, just convert into an array as well, as in below code
app.post("/data", uploads, function (req, res) {
User.findById(req.user.id, function (err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
// you can put more validation if you want, for empty or any other syntax
if (Array.isArray(req.body.checkbox)) {
res.render("checkout",{SIMG: req.body.checkbox});
} else {
res.render("checkout",{SIMG: [req.body.checkbox]});
}
}
}
});
});

Related

Node.js display all images of a user in ejs from database

I am new to Node.js and right now I am trying to display all my images of a logged in user from my database to my ejs file. I have set up all the code but i have a problem where I don't know how to properly display all images. I have also tried async functions but i don't seem to do them the right way. I have tried multiple thinks like for...of and forEach.
I noticed that if i do a try catch i get the error Cannot set headers after they are sent to the client which is probably because i can't use res.render multiple times. Alongside this error I also get this error: UnhandledPromiseRejectionWarning: TypeError: req.next is not a function even if I don't use try catch. At this point I don't know what is the proper way to display all images so I came here for help. The problem is that i don't realy properly understand the async functions and if they should be used in this case.
The for loop works fune and the problem accurs when I try res.render, because if i console.log(img) I get all the images of that user. The problem is also that I can display 1 image but i cant display multiple
Have gone through multiple documentations and cases and didn't find a solution so I came here
app.get('/galery/galery', (req, res) => {
const selectImg = conn.query("SELECT pic_name, user_id FROM galery WHERE user_id =?", [req.session.userId], function (err, result){
if (result && result.length) {
for(let i = 0; i < result.length; i++){
var imgName = await result[i].pic_name
var img = 'upload/' + imgName
res.render('./galery/galery', {img: img, imgName: imgName})
console.log(img)
}
return
res.end()
} else {
res.render('./galery/galery')
}
})
})
and this is my ejs file
<div class="galery">
<% if (typeof img !== 'undefined') {%>
<img src= "<%= img %>" alt="img" width="600" height="400">
<div class="style"><%= imgName %>
<form action="deleteImg.php" method="post">
<input type="hidden" name="deleteImg" value="">
<button class="btn btn-danger" name="delete" type="submit" >delete</button>
</input>
</form>
</div>
<% }%>
</div>
As far as I can tell your problem is that you render (as u said) multiple times inside the loop. That is not how it suppose to happen. You have to render only once.
You are getting image data correctly, then modify it in a way you want and collect at one place, once done render it with the new data.
app.get('/galery/galery', (req, res) => {
const query = 'SELECT pic_name, user_id FROM galery WHERE user_id =?';
conn.query(query, [req.session.userId], function (err, result) {
if (result && result.length) {
const images = [];
for (const item of result) {
const imgName = item.pic_name;
const img = 'upload/' + imgName;
// { img, imgName } is shorthand for { img: img, imgName: imgName }
images.push({ img, imgName });
}
res.render('./galery/galery', { images });
} else {
res.render('./galery/galery', { images: []});
}
});
});
Now in ejs side, you have a list of images already and you need to iterate over it to display correctly.
<% for(const image of images) { %>
<div class="galery">
<img src="<%= image.img %>" alt="img" width="600" height="400">
<div class="style"><%= image.imgName %>
<form action="deleteImg.php" method="post">
<input type="hidden" name="deleteImg" value="">
<button class="btn btn-danger" name="delete" type="submit" >delete</button>
</form>
</div>
</div>
<% } %>

How to use ejs with json data?

I want to use a json to read my videos and then display them using ejs, but I get an error:
>> 320| <% video.videos.forEach(function(video) { %>
video is not defined
I've used the same approach for the items.json and I did not have this problem, if needed I can upload the code for displaying the items and the items.json too.
HTML:
<ul class="splide__list">
<% video.videos.forEach(function(video) { %>
<li class="splide__slide">
<a href="<%= video.href %>" data-lity>
<img class="thumbnail" data-splide-lazy="<%= video.src %>" alt="<%= video.alt %>">
</a>
<p><%= video.desc %></p>
</li>
<% }) %>
</ul>
Node js:
app.all('/', function (req, res) {
var items, videos;
//read shop items
fs.readFile('items.json', function (err, data) {
if (err) {
res.status(500).end();
} else {
items = JSON.parse(data);
}
});
// read videos
fs.readFile('video.json', function (err, data) {
if (err) {
res.status(500).end();
} else {
videos = JSON.parse(data);
}
});
res.render('index.ejs', {
items: items,
videos: videos
});
});
My video.json:
{
"videos":[
{
"href":"media/video.mp4",
"src":"thumbnails/thumbnail2.png",
"alt":"video with 1",
"desc":"desc1"
},
{
"href":"media/video3.mp4",
"src":"thumbnails/thumbnail3.png",
"alt":"video with 2",
"desc":"desc2"
}
]
}
The problem is that fs.readFile is async, whereas you call the render function without waiting for them, so you have no warranty that you will have the content read into memory. To solve the problem you can either move your render call into callback or use async/await to handle async code. I will show you the example with async/await.
app.all('/', function (req, res) {
try {
const items = await fs.promises.readFile('items.json').then(JSON.parse);
const videos = await fs.promises.readFile('videos.json').then(JSON.parse);
res.render('index.ejs', { items, videos });
} catch (e) {
res.status(500).end();
}
});
And yes you are passing videos whereas in template you access the video variable so change that to the videos too.

How can I pass 'hello' to render a dynamic html page like the 'user' variable? My cb is broken now. Very new to this

app.get('/indexInstHome.html',
require('connect-ensure-login').ensureLoggedIn('indexErrorAccount.html'),
function(req, res, cb)
{
test.find({selector:{"Class.email": req.user._id }}, function(er,
result, cb) {
if (er) {
console.log(er);
}
hello = result.docs;
return cb(hello);
});
hello = cb();
console.log(hello);
res.render('indexInstHome.html', { user: req.user, Classes: hello });
});
I'm not sure what test.find does but I'm assuming it's some sort of asynchronous DB query. I don't know what the third argument (the callback) would be used for but I don't think it's needed for what you're trying to do.
I suggest using the name next for the Express callback as that convention is pretty much universal, you'll only confuse other people if you start calling it cb.
The main problem with your code seems to be a misunderstanding about how callbacks work with asynchronous code. It's much simpler than you're trying to make it, you just need to wait for the asynchronous process to finish before you try to render your page. You do that by putting the relevant code inside the callback function, i.e. the function that you pass to find.
I've had to make a few assumptions about what you're trying to do but I think it'd be something like this:
app.get('/indexInstHome.html',
require('connect-ensure-login').ensureLoggedIn('indexErrorAccount.html'),
function(req, res, next) {
test.find({selector: {"Class.email": req.user._id}}, function(err, result) {
if (err) {
console.log(err);
next(err);
}
else {
// Assuming result cannot be null
var hello = result.docs;
console.log(hello);
res.render('indexInstHome.html', { user: req.user, Classes: hello });
}
});
}
);

HTML POST Request doesn't send all the data

I am using Node.js + Express.js to build a very basic search engine for a Library database. My HTML has a POST form that sends either a title or keyword to the app in order to query a MySQL database for the book/books. The search by title is working fine but the keyword is giving me a hard time. See below.
HTML
<form id="searchForm" action="" method="post">
<select class="form-control" id="searchType">
<option class="" value="0">Search by title</option>
<option class="" value="1">Search by keyword</option>
</select>
<select class="form-control" id="titles" name="titles">
<% for (var title in titles) {;%>
<option class=""><%=titles[title].TITLE%></option>
<% }; %>
</select>
<textarea class="form-control" name="keyword" contenteditable="false" id="keyword" placeholder="Enter keyword here..."></textarea> <!-- placeholder is only supported in a few browesers
(Firefox 3.7+, Chrome, Safari, IE 10). Could use
jQuery but ah-->
<input class="btn btn-default" type="submit" value="Search"></input>
</form> <!-- /searchForm -->
Express code
app.post('/getBooksByTitle', function (req, res) {
connection.getConnection(function(err, tempCon) {
if (err) {
tempCon.release();
console.log('ERROR IN SQL CONNECTION!');
}
else {
console.log('CONNECTED TO SQL');
tempCon.query('SELECT * FROM BOOKS WHERE TITLE=?', req.body.titles, function(err, rows) {
if (err) {
console.log('BAD QUERY');
}
else {
console.log(req.body.titles);
res.json(rows);
}
tempCon.release();
});
}
});
});
app.post('/getBooksByKeyword', function (req, res) {
connection.getConnection(function(err, tempCon) {
if (err) {
tempCon.release();
console.log('ERROR IN SQL CONNECTION!');
}
else {
console.log('CONNECTED TO SQL');
tempCon.query('SELECT * FROM BOOKS WHERE (AUTHOR LIKE ? || TITLE LIKE ? || GENRE LIKE ? || DESCRIPTION LIKE ?)', '%' + req.body.keyword + '%', function(err, rows) {
if (err) {
console.log('BAD QUERY');
console.log(req.body);
}
else {
res.json(rows);
console.log(req.body.keyword);
}
tempCon.release();
});
}
});
});
I am pulling the form data to node with req.body.(field_name) but it doesn't seem to gather the text box. If I console.log(req.body) I see only the title field. Where have I messed up?
EDIT: The jQuery script that handles the action and some animations.
$(document).ready(function () {
toggleFields(); //call this first so we start out with the correct visibility depending on the selected form values
//this will call our toggleFields function every time the selection value of our searchType field changes
$("#searchType").change(function () {
toggleFields();
});
});
//this toggles the visibility of our input fields depending on the current selected value of the searchType field.
//also it toggles the action of the submit form to the appropriete post call.
function toggleFields() {
if ($("#searchType").val() == 0) {
$("#searchForm").attr('action', '/getBooksByTitle');
$("#titles").slideDown();
$("#keyword").slideUp();
}
else {
$("#searchForm").attr('action', '/getBooksByKeyword');
$("#titles").slideUp();
$("#keyword").slideDown();
}
}
Thanks for the help you tried to provide. I fixed the problem by changing the <textarea> field to an <input> field. Now the post sends both my title and keyword back to the server.

How to display console.log results from API requests

//Express
var express = require('express');
var server = express();
//Steam
var SteamWebAPI = require('steamwebapi').SteamWebAPI;
SteamWebAPI.setAPIKey('XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
//Steam - Recently Played Games
server.get('/games', function (req, res) {
SteamWebAPI.getRecentlyPlayedGames('76561198190043289', 5, function(response) {
res.json(response.response.games);
});
});
//Localhost
server.listen(3000, function () {
console.log('Server: ');
});
This is my output:
[{"appid":730,"name":"Counter-Strike: Global Offensive","playtime_2weeks":620,"playtime_forever":4016,"img_icon_url":"69f7ebe2735c366c65c0b33dae00e12dc40edbe4","img_logo_url":"d0595ff02f5c79fd19b06f4d6165c3fda2372820"}]
Ok, if you go to website: https://steamid.io/ ,you enter your name, press button 'lookup', and you get your IDs. How do I make that? Is it possible to make that user enter his name/id and he get informations. This way I'm only one who can enter user, and he gets text, images according to his request.
Like, I learned a lot about Steam Web Api these days but I still haven't figured out how to display data. Some cool guy helped me, but we used Angular, I'm still not familiar with it, I'm planning to learn Ember.js these days, but are they any alternatives?
If you wanna do as your example, it seems you miss to do the front part, and some back end too.
First you have to create a form. In your case, just an input to ask the user ID and a submit button could be enough. Then you have created your form in a html file, render it with the render function gave by Express framework (http://expressjs.com/en/4x/api.html#app.render)
And a light reminder on the html form (http://www.w3schools.com/html/html_forms.asp)
But you have to render it, to a specific URL, for example
// Render your basic form
// The form is in the form.html file
// By default, views have to be placed into the view or views folder, I don't know exactly no more
server.get('/ask_user_id', function (req, res) {
app.render('form', function(err, html){
if(err) return res.send(err);
else return res.send(html)
});
});
So if you go on localhost:3000/ask_user_id your form will be rendered
Second when a user write its ID into your input and submit the form, you have to retrieve theses info, and then call your steam API. So, if the action of your form is a POST, and the Url to submit is /user_infos, and the input for user ID's name is userId, here will be the code.
// Render your basic form
// The form is in the form.html file
// By default, views have to be placed into the view or views folder, I don't know exactly no more
server.get('/ask_user_id', function (req, res) {
app.render('form', function(err, html){
if(err) return res.send(err);
else return res.send(html)
});
});
// You have to have a route that handle your form submission
server.post('/user_infos', function (req, res) {
// All your form submitted is in your req.body
// So retrieve the userID
var _userID = req.body.userId;
// Then sent the variable to the SteamAPI
SteamWebAPI.getRecentlyPlayedGames(_userID, 5, function(response) {
return res.json(response.response.games);
});
});
Hope it helps
In your controller/Factory, make sure you have a callback function to get the response from the server.
$scope.postData = function(){
$http.post('/', {data: someData})
.then(function(response){
console.log(JSON.stringify(response.data));
});