I'm trying to implement server side pagination with NodeJS/Express. But the frontend rendering of it isn't working as expected. I fetch from a third-party API and implement the logic for the pagination. I then connect it to the frontend for rendering.
I have created cards to render the information from the API, first time remove the cards to render next page elements/cards with the code:
if(card !== undefined){
card.remove();
}
It works and it removes the cards, you can see this in the frontend, first eventlistener. But when I try to implement the same in the second eventlistener the elements/cards aren't removed and replaced with new ones.
Pagination - Backend
const express = require('express');
const router = express.Router();
const fetch = require('node-fetch');
router.get('/pokemon', function(req, res) {
async function getPokemonInfo(){
return fetch('https://pokeapi.co/api/v2/pokemon?limit=251')
.then(response => response.json())
.then(data => {
getDetailsPokemon(data)
}).catch(error => {
console.log(error);
})
}
getPokemonInfo()
async function getDetailsPokemon(pokemon){
const pokemonArr = pokemon.results;
const eachPokemon = pokemonArr.map(pokeEl => {
return fetch(pokeEl.url)
.then(response => response.json())
.catch(error => {
console.log(error);
})
})
Promise.all(eachPokemon)
.then(values => {
const page = parseInt(req.query.page);
const limit = parseInt(req.query.limit);
console.log(page, limit);
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
console.log(startIndex, endIndex);
const results = {}
if(endIndex < values.length){
results.next = {
page: page + 1,
limit: limit
}
}
if(startIndex > 0){
results.previous = {
page: page - 1,
limit: limit
}
}
results.result = values.slice(startIndex, endIndex);
console.log(results);
res.json(results)
})
}
})
module.exports = router;
Pagination - Frontend
const paginatedApp = {
paginatedPokemon: async function(){
await fetch(`http://localhost:1337/pokemon?page=1&limit=20`)
.then(response => response.json())
.then(data => {
this.createCards(data)
})
},
createCards: function(pokemonObj){
let next = document.querySelector('.next');
let previous = document.querySelector('.previous');
pokemonObj.result.map(pokeEl => {
// Variables
let main = document.querySelector('main');
let card = document.createElement('div');
let imgHolder = document.createElement('div');
let img = document.createElement('img');
let infoHolder = document.createElement('div');
let infoBoxOne = document.createElement('div');
let infoBoxTwo = document.createElement('div');
let pId = document.createElement('p');
let pName = document.createElement('p');
let boxForType = document.createElement('div');
let id = document.createTextNode('id:');
let name = document.createTextNode('name:');
let type = document.createTextNode('type:');
// Info box 1
let infoPId = document.createElement('p');
let infoPName = document.createElement('p');
let infoPType = document.createElement('p');
infoPId.append(id);
infoPName.append(name);
infoPType.append(type);
infoBoxOne.append(infoPId, infoPName, infoPType);
infoBoxTwo.append(pId, pName, boxForType);
// Lägg till klasser
card.classList.add('card');
imgHolder.classList.add('imgHolder');
infoHolder.classList.add('infoHolder');
boxForType.classList.add('boxForType');
img.classList.add('img');
infoHolder.classList.add('infoHolder');
pId.classList.add('id');
pName.classList.add('name');
infoBoxOne.classList.add('infoBoxOne');
infoBoxTwo.classList.add('infoBoxTwo');
img.src = pokeEl.sprites.other['official-artwork']['front_default'];
pId.append(`#${pokeEl.id}`);
pName.append(pokeEl.name);
// Types
pokeEl.types.map(pokeType => {
let pokemonType = pokeType.type.name
let pokes = document.createElement('p');
pokes.append(pokemonType)
boxForType.append(pokes);
})
imgHolder.appendChild(img);
card.appendChild(imgHolder)
main.appendChild(card);
infoHolder.append(infoBoxOne)
infoHolder.append(infoBoxTwo)
card.appendChild(infoHolder);
boxForType.children[0].style.border = '1px solid #fff';
boxForType.children[1] ? boxForType.children[1].style.border = '1px solid #fff' : console.log('boxForType.children[1] dosen\'t exist for this post');
next.addEventListener('click', () => {
if(card !== undefined){
card.remove();
}
})
})
// Second next eventlistener
next.addEventListener('click', () => {
fetch(`http://localhost:1337/pokemon?page=${pokemonObj.next.page++}&limit=20`)
.then(response => response.json())
.then(data => {
data.result.map(dataEl => {
let main = document.querySelector('main');
let card = document.createElement('div');
if(card !== undefined){
card.remove();
}
let imgHolder = document.createElement('div');
let img = document.createElement('img');
let infoHolder = document.createElement('div');
let infoBoxOne = document.createElement('div');
let infoBoxTwo = document.createElement('div');
let pId = document.createElement('p');
let pName = document.createElement('p');
let boxForType = document.createElement('div');
let id = document.createTextNode('id:');
let name = document.createTextNode('name:');
let type = document.createTextNode('type:');
// Info box 1
let infoPId = document.createElement('p');
let infoPName = document.createElement('p');
let infoPType = document.createElement('p');
infoPId.append(id);
infoPName.append(name);
infoPType.append(type);
infoBoxOne.append(infoPId, infoPName, infoPType);
infoBoxTwo.append(pId, pName, boxForType);
// Lägg till klasser
card.classList.add('card');
imgHolder.classList.add('imgHolder');
infoHolder.classList.add('infoHolder');
boxForType.classList.add('boxForType');
img.classList.add('img');
infoHolder.classList.add('infoHolder');
pId.classList.add('id');
pName.classList.add('name');
infoBoxOne.classList.add('infoBoxOne');
infoBoxTwo.classList.add('infoBoxTwo');
img.src = dataEl.sprites.other['official-artwork']['front_default'];
pId.append(`#${dataEl.id}`);
pName.append(dataEl.name);
// Types
dataEl.types.map(pokeType => {
let pokemonType = pokeType.type.name
let pokes = document.createElement('p');
pokes.append(pokemonType)
boxForType.append(pokes);
})
imgHolder.appendChild(img);
card.appendChild(imgHolder)
main.appendChild(card);
infoHolder.append(infoBoxOne)
infoHolder.append(infoBoxTwo)
card.appendChild(infoHolder);
if(card !== undefined){
card.remove();
}
})
})
})
}
}
paginatedApp.paginatedPokemon();
Related
Repo: https://github.com/bigolboyyo/weatherdbapp
Hello! I am working on a small project working with an API for the first time. I'm making a simple weather app that allows you to type in a location and append a "card" of data.
This card has a "front" and "back". Front being the weather data and the back being a simple textarea notepad.
As far as functionality goes I am just about there. My only issue is now when I have more than one card I only get an event listener on the first card button and the first card's button affects all the other cards created after! How would I apply the event listener for each card's buttons made and have those buttons only affect the relevant card?
Current code I'm working with
let notesBtn = document.getElementById("notes-btn");
notesBtn.addEventListener("click", handleFrontClick);
console.log(notesBtn);
function handleFrontClick(e) {
e.target.parentNode.remove();
card.appendChild(back);
let frontBtn = document.getElementById("front-btn");
frontBtn.addEventListener("click", (e) => {
e.target.parentNode.remove();
card.appendChild(front);
});
ALL JS (git repo above)
const body = document.getElementsByTagName("body");
const unsplashRandom =
"https://source.unsplash.com/random/1920x1080/?landscapes";
document.body.style.backgroundImage = `url(${unsplashRandom})`;
const header = document.createElement("div");
header.className = "header";
header.id = "header";
document.body.append(header);
const container = document.createElement("div");
container.className = "container";
document.body.append(container);
const h1 = document.createElement("h1");
h1.id = "h1";
h1.textContent = "WeatherDB Search";
h1.style.textAlign = "center";
header.append(h1);
let p1 = document.createElement("p");
p1.id = "p1";
p1.textContent = "Enter location for today's weather!";
p1.style.textAlign = "center";
header.append(p1);
const cityInput = document.createElement("input");
cityInput.className = "search-inputs";
cityInput.id = "city-input";
cityInput.placeholder = " ex: New York, Toronto, London";
header.append(cityInput);
const searchButton = document.createElement("button");
searchButton.className = "buttons";
searchButton.id = "search-btn";
searchButton.textContent = "🔍";
let br = document.createElement("br");
header.append(br);
header.append(searchButton);
function fetchWeather(e) {
let userInput = cityInput.value;
let config = {
Headers: {
"Content-type": "application/json",
},
};
return fetch(
`https://weatherdbi.herokuapp.com/data/weather/${userInput}`,
config
)
.then((res) => res.json())
.then((json) => weatherSearch(json, userInput))
.catch((err) => {
window.alert(
`${err} \n https://weatherdbi.herokuapp.com/data/weather/${userInput}`
);
console.log(err);
});
}
function weatherSearch(e, userInput) {
cityInput.value = "";
let weatherKeys = Object.keys(e.currentConditions);
let weatherValues = Object.values(e.currentConditions);
let temps = Object.values(weatherValues[1]);
let speeds = Object.values(weatherValues[4]);
const card = document.createElement("div");
card.id = `${userInput}-card`;
card.className = "cards";
container.append(card);
const front = document.createElement("div");
front.id = `${userInput}-front`;
front.className = "front";
front.innerHTML = `
<h1>${userInput.toUpperCase()}</h1>
<p>Time: ${weatherValues[0]}</p>
<li id="comment">Comment: ${weatherValues[6]}</li>
<li id="precipitation">Precipitation: ${weatherValues[2]}
<li id="temp">Temp: ${temps[0]}c | ${temps[1]}f</li>
<li id="humidity">Humidity: ${weatherValues[3]}</li>
<li id="wind">Wind Speed: ${speeds[0]}km | ${speeds[1]}mile</li>
</br>
<button id="notes-btn" class="buttons">Notes</button>
`;
card.append(front);
const back = document.createElement("div");
back.id = `${userInput}-back`;
back.className = "back";
back.innerHTML = `
<textarea id="back-text" name="backtext"
rows="25" placeholder="NotePad">
</textarea>
</br>
<button id="front-btn">Flip</button>
`;
let notesBtn = document.getElementById("notes-btn");
notesBtn.addEventListener("click", handleFrontClick);
console.log(notesBtn);
function handleFrontClick(e) {
e.target.parentNode.remove();
card.appendChild(back);
let frontBtn = document.getElementById("front-btn");
frontBtn.addEventListener("click", (e) => {
e.target.parentNode.remove();
card.appendChild(front);
});
}
}
document.addEventListener("DOMContentLoaded", (e) => {
searchButton.addEventListener("click", (e) => {
fetchWeather();
});
document.addEventListener("keydown", function (e) {
if (e.key === "Enter" && cityInput.value.length > 0) {
fetchWeather();
}
});
});
I'm having trouble implementing the logic that will limit me from adding the same items to my shopping list. When the item is the same, I just want to display the quantity with the existing item.
<div class="pizzas">
</div>
<div class="shoppingCart">
<p class="totalPrice">Hungry? order our pizzas</p>
</div>
// js
fetch("https://raw.githubusercontent.com/alexsimkovich/patronage/main/api/data.json")
.then(data => data.json())
.then(data => {
let valueCurrency = 0;
data.forEach(element => {
const shoppingCart = document.querySelector(".shoppingCart");
const pizzas = document.querySelector(".pizzas");
const box = document.createElement("div");
const img = document.createElement("img");
const title = document.createElement("h3");
const ingredients = document.createElement("p");
const price = document.createElement("h4");
const btn = document.createElement("button");
const totalPrice = document.querySelector(".totalPrice");
box.className = "box";
ingredients.className = "ingredients"
btn.className = "btn";
img.src = element.image;
img.className = "img";
title.innerHTML = element.title;
ingredients.innerHTML = element.ingredients;
price.innerHTML = element.price.toFixed(2) + " zł";
btn.innerHTML = "Dodaj do koszyka";
box.appendChild(img);
box.appendChild(title);
box.appendChild(ingredients);
box.appendChild(price);
box.appendChild(btn);
pizzas.appendChild(box);
btn.addEventListener("click", (e) => {
valueCurrency = valueCurrency + element.price;
const pizza = document.createElement("div");
pizza.className = "pizzaList";
const pizzasList = document.createElement("li");
const pizzaPrice = document.createElement("p");
const btnRemove = document.createElement("button");
btnRemove.innerText = "X";
pizzasList.innerText = title.textContent;
pizzaPrice.innerText = price.textContent;
pizza.appendChild(pizzasList);
pizza.appendChild(pizzaPrice);
pizza.appendChild(btnRemove);
totalPrice.innerText = "Całkowita cena: " + valueCurrency.toFixed(2);
if(pizzasList.innerText === pizzasList.innerText)
{
// don't add another item to the list
// just add +1 to existing element
}
else
{
// add an item to the list
shoppingCart.prepend(pizza);
}
btnRemove.addEventListener("click", (e) => {
pizza.remove();
valueCurrency = valueCurrency - element.price;
totalPrice.innerText = "Całkowita cena: " + valueCurrency.toFixed(2);
})
})
});
})
.catch(err => console.log(err));
My problem is exactly in the conditional statement, I don't know exactly how to implement the counting of the same pizzas option.
Thank you in advance for your help.
Since you are using html elements for this, what you can do is to use a data-attribute in your pizza element and increment it each time you need.
Something like:
if(pizzasList === pizzasList)
{
pizza.dataset.total = Number(pizza.dataset.total) + 1;
}
else
{
pizza.dataset.total = 1;
shoppingCart.prepend(pizza);
}
Then just use pizza.dataset.total to retieve the total number of repetitions.
I'm making a ticket bot where a user creates a ticket and then gets the support they need then after an admin or the same user closes the ticket a transcript will be saved and sent in the same channel and I've already done that but the only thing I'm struggling at is displaying Embeds and Images in that transcript log. Now it looks like this (Copy paste it to see):
file:///G:/BotTickets/Akeoe%20792817221239832627.html
I figured maybe the image would help you understand more, all the bots( ASOG ) messages is Embeds and it looks like that with images too
Code of how I save and display:
let messageCollection = new Discord.Collection();
let channelMessages = await channel.messages.fetch({
limit: 100
}).catch(err => console.log(err));
messageCollection = messageCollection.concat(channelMessages);
while(channelMessages.size === 100)
{
let lastMessageId = channelMessages.lastKey();
channelMessages = await channel.messages.fetch()({ limit: 100, before: lastMessageId }).catch(err => console.log(err));
if(channelMessages)
messageCollection = messageCollection.concat(channelMessages);
}
let msgs = messageCollection.array().reverse();
let data = await fs.readFile('./commands/template.html', 'utf8').catch(err => console.log(err));
if(data)
{
await fs.writeFile(`G:\BotTickets/${ticketUserusername + ` ` + channel.id}.html`, data).catch(err => console.log(err));
let guildElement = document.createElement('div');
let guildText = document.createTextNode(message.guild.name);
let guildImg = document.createElement('img');
guildImg.setAttribute('src', message.guild.iconURL());
guildImg.setAttribute('width', '150');
guildElement.appendChild(guildImg);
guildElement.appendChild(guildText);
console.log(guildElement.outerHTML);
await fs.appendFile(`G:\BotTickets/${ticketUserusername + ` ` + channel.id}.html`,
guildElement.outerHTML).catch(err => console.log(err));
msgs.forEach(async msg => {
let parentContainer = document.createElement("div");
parentContainer.className = "parent-container";
let avatarDiv = document.createElement("div");
avatarDiv.className = "avatar-container";
let img = document.createElement('img');
img.setAttribute('src', msg.author.displayAvatarURL());
img.className = "avatar";
avatarDiv.appendChild(img);
parentContainer.appendChild(avatarDiv);
let messageContainer = document.createElement('div');
messageContainer.className = "message-container";
let nameElement = document.createElement("span");
let name = document.createTextNode(msg.author.tag + " " + msg.createdAt.toDateString() + " " +
msg.createdAt.toLocaleTimeString() + " EST");
nameElement.appendChild(name);
messageContainer.append(nameElement);
if(msg.content.startsWith("```"))
{
let m = msg.content.replace(/```/g, "");
let codeNode = document.createElement("code");
let textNode = document.createTextNode(m);
codeNode.appendChild(textNode);
messageContainer.appendChild(codeNode);
}
else
{
let msgNode = document.createElement('span');
let textNode = document.createTextNode(msg.content);
msgNode.append(textNode);
messageContainer.appendChild(msgNode);
}
parentContainer.appendChild(messageContainer);
await fs.appendFile(`G:\BotTickets/${ticketUserusername + ` ` + channel.id}.html`,
parentContainer.outerHTML).catch(err => console.log(err));
});
}
I’m building a site using sapper and requesting data from an API. It has been working smooth until now.
When I’m going from site.com/title/id1 to site.com/title/id2 the new information is not loaded until I hit a manual refresh. Any ideas?
import { stores, goto } from "#sapper/app";
import Card from "../_titlecard.svelte";
const { page } = stores();
const { slug } = $page.params;
import { onMount } from "svelte";
let looper = [];
let artistName = "";
let titleName = "";
let dvdCover = "";
let titleCover = "";
let genre = "";
let tracks = [];
onMount(async () => {
const res = await fetch(`https://.com/api/title/${slug}`);
const data = await res.json();
artistName = data.artistName;
titleName = data.name;
dvdCover = data.graphics.dvd;
titleCover = data.graphics.landscape;
genre = data.genre;
tracks = data.tracks.length;
const res2 = await fetch(`https://.com/api/artists/all`);
const data2 = await res2.json();
let moreTitles = [];
const more = data2.map((x) => {
if (x.titles.length > 0 && x.genre === genre) {
looper.push(x.titles[0]);
looper = moreTitles;
}
});
});
And then I have this in the html
{#each looper.slice(0, 4) as item, i}
<Card imgurl={item.graphics.dvd} concert={item.name} id={item.id} />
{/each}
A page component is not unmounted and mounted again if the navigation results in the same page component being used, so your onMount will only be run once with the first id.
You could use a reactive statement to make sure you run the desired code every time $page.params.slug changes.
Example
import { stores, goto } from "#sapper/app";
import Card from "../_titlecard.svelte";
const { page } = stores();
let looper = [];
let artistName = "";
let titleName = "";
let dvdCover = "";
let titleCover = "";
let genre = "";
let tracks = [];
$: (async () => {
const { slug } = $page.params;
const res = await fetch(`https://.com/api/title/${slug}`);
const data = await res.json();
artistName = data.artistName;
titleName = data.name;
dvdCover = data.graphics.dvd;
titleCover = data.graphics.landscape;
genre = data.genre;
tracks = data.tracks.length;
const res2 = await fetch(`https://.com/api/artists/all`);
const data2 = await res2.json();
let moreTitles = [];
data2.forEach((x) => {
if (x.titles.length > 0 && x.genre === genre) {
moreTitles.push(x.titles[0]);
}
});
looper = moreTitles;
})();
The logic seems sound but my ul is not displaying what I am asking it to. I have used console.logs and I am for sure getting poem in the function displayPoem(poem) but it isn't showing up when I button click. Any help would be greatly appreciated!
const inputsList = document.querySelector('ol');
const poemsList = document.getElementById('savedThoughts');
const form = document.getElementById('')
const submitButton = document.getElementById('submitThoughts');
const startButton = document.querySelector('#startButton')
startButton.onclick = () => {
const ranNum = generateRanNum();
generateInputs(ranNum)
changeToRestartText()
}
submitButton.onclick = () => {
const poem = savePoem();
console.log(poem)
displayPoem(poem);
clearForm()
}
const generateRanNum = () => {
let randomNumber = Math.floor(Math.random() * 20);
return randomNumber
}
const changeToRestartText = () => {
startButton.textContent = 'Restart Game'
}
const generateInputs = (ranNum) => {
const listItem = document.createElement('li');
for(let i = 1; i <= ranNum; i++){
const input = document.createElement('input');
listItem.appendChild(input).setAttribute('type', 'text');
console.log(ranNum)
}
inputsList.appendChild(listItem);
}
const savePoem = () => {
let poemArr = [];
const input = document.querySelectorAll('input');
input.forEach(element => {
poemArr.push(element.value);
})
// console.log(poemArr)
return poemArr;
}
const displayPoem = (poem) => {
const savedPoem = document.createElement('li')
const savedText = document.createElement('span')
const deletePoem = document.createElement('button')
console.log(poem)
savedPoem.appendChild(savedText);
savedText.textContent = poem.toString();
savedPoem.appendChild(deletePoem);
deletePoem.textContent = 'Delete';
poemsList.appendChild(savedPoem)
deletePoem.onclick = e => {
poemsList.removeChild(savedPoem);
}
}
const clearForm = () => {
const inputLi = document.querySelectorAll('li');
inputLi.forEach(element => {
element.remove()
})
}
small html segment
<div >
<ul id="savedThoughts">
</ul>
</div>
Your saved list items aren't showing up because your submit onclick calls displayPoem which creates list items and then calls clearForm which removes all list items on the page. Try inputLi = document.querySelectorAll('ol > li').