How to use ejs with json data? - html

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.

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>
<% } %>

EJS not displaying value if only single item passed

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]});
}
}
}
});
});

How get data lists from json using Vue axios infinite scroll

I would like to import the data from Vue.js to axios and apply the infinite scroll.
and want to have json data displayed in order. Now only index [0] is being repeated. How can we solve it? Thank you.
https://jsfiddle.net/naeun/eywraw8t/131773/
<div id="app">
<section data-aos="fade-zoom-in" v-for="post in posts">
<div class="post">
<p class="colon" data-aos="fade-zoom-in"><span>“</span></p>
<p class="quote">{{post.quote}}</p>
<p class="colon" data-aos="fade-zoom-in"><span>”</span></p>
<p class="author" data-aos="fade-zoom-in">{{post.author}}</p>
</div>
</section>
</div>
new Vue({
el: "#app",
data: {
bottom: false,
posts: []
},
methods: {
bottomVisible() {
const scrollY = window.scrollY;
const visible = document.documentElement.clientHeight;
const pageHeight = document.documentElement.scrollHeight;
const bottomOfPage = visible + scrollY >= pageHeight;
return bottomOfPage || pageHeight < visible;
},
addPost() {
axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then(response => {
let api = response.data[0];
let apiInfo = {
author: api.id,
quote: api.title,
tag: api.body
};
this.posts.push(apiInfo)
if(this.bottomVisible()) {
this.addPost();
}
})
.catch(e => {
console.log('Error: ', error)
})
}
},
watch: {
bottom(bottom) {
if (bottom) {
this.addPost();
}
}
},
created() {
window.addEventListener('scroll', () => {
this.bottom = this.bottomVisible()
});
this.addPost();
}
})
There are a few problems here. First, whenever you scroll to the bottom, you call the addPost method, right? But the method itself doesn't know which "page" to load. It does the very same request over and over again. Which means it gets the same results each time.
Then you use this let api = response.data[0];, which means that no matter what results you receive, you only get the first item from the list and push it to your local array.
What you need to do is to keep track of the virtual "page" that you are loading, meaning that each addPost is like loading additional items from a virtual pagination and just putting them at the end of the infinite list, instead of reloading the page. Then you need to pass this parameter to the method that loads those new items and prepare the backend to return specific items based on request parameters.
Good luck!

How to display image returned in JSON response after uploading

I have a page where you can upload images. Everything works except I don't know how to make the page refresh with the new image which is returned in the json response on success. I cant use location.reload() because it refreshes the whole app and starts off from the home page. I am using angular2/typescript for my frontend
component.ts (The image value is returned on success)
uploadFile(): any {
let file = this.fileInput.nativeElement;
if (file.files && file.files[0]) {
let fileToUpload = file.files[0];
this.getService
.uploadImg(fileToUpload)
.subscribe(
response => {
},
err => {
console.log("ERR", err)
}
},
() => console.log("Success")
);
}
My solution would be to update the src attribute of the img container. This can be done using the setAttribute function. A primitive example is below.
Javascript
uploadFile(): any {
let file = this.fileInput.nativeElement;
if (file.files && file.files[0]) {
let fileToUpload = file.files[0];
this.getService
.uploadImage(fileToUpload, this.id)
.subscribe(
response => {
this.image = response.image;
document.getElementbyId("image-id").setAttribute("src", "data:image/JPG;base64," + pic.pic );
},
err => {
console.log("ERR", err)
}
},
() => console.log("Success")
);
}
HTML
<div *ngIf="pic.pic">
<img id="image-id" [src]="'data:image/JPG;base64,' + pic.pic" />
</div>
Heres how I solved it.
component.ts (The image value is returned on success)
pic: any;
upload: any {
let file = this.fileInput.nativeElement;
if (file.files && file.files[0]) {
let fileToUpload = file.files[0];
this.getService
.uploadImg(fileToUpload)
.subscribe(
response => {
//Assign the response here
this.pic = response.image;
},
err => {
console.log("ERR", err)
}
},
() => console.log("Success")
);
}
The html page
<img [src]="'data:image/JPG;base64,' + pic " />

How to modify json data in after storing in vue data object

This is my vue code :
new Vue({
el : '#root',
data : {
blog : []
},
created() {
this.$http.get('https://jsonplaceholder.typicode.com/posts')
.then(function(response) {
// console.log(response.data)
this.blog = response.data
})
.catch(function (error) {
this.error = 'Error! Could not reach the API. ' + error
})
}
});
My html code is :
<div id="root" class="container">
<ul v-for="post in blog">
<li> {{ post.id }} </li>
<li>{{ post.userId }} </li>
<li>{{ post.title }} </li>
</ul>
</div>
Now I can show every user's name just fine, but I want to modify something like if user id is 1 then the user's name will be changed into "Smith".
I tried this code:
mounted() {
if (this.blog[0].userId == 1) {
this.blog[0].userId = 'Smith'
}
}
But it shows this error :
Uncaught TypeError: Cannot read property 'userId' of undefined
If I used in method with event it works just fine! How to do this ?
After console.log(this.blog)
Also after console.log(this.blog[0].userId) I get : "1"
Problem is that your code in mounted() method done before you push response.data in blog array. So that's why it can't read any of properties.
You can call methods after you fetch data, in then() callback to be sure that you have data in blog array and then call methods for working with a blog:
new Vue({
el: "#vue",
data() {
return {
blog: []
};
},
methods: {
changeNames() {
if (this.blog[0].userId == 1) {
this.blog[0].userId = "Smith";
}
}
},
created() {
Vue.http
.get("https://jsonplaceholder.typicode.com/posts")
.then(response => {
this.blog = response.data;
this.changeNames();
})
.catch((error) => {
this.error = "Error! Could not reach the API. " + error;
});
}
});
Here is working example: jsFiddle