I am stuck at the part that fetch more data when user clicks a button on bottom of a page using Svelte and Sapper.
Here is the code.
<script context="module">
export function preload(page) {
return this.fetch(`https://reqres.in/api/users?page=${$count}`) // I know this is not goint to work. but when the button is clicked, I want to fetch page 2 and merge it with page 1 data, show data in ascending order(page1, page2)
.then(res1 => {
return res1.json()
}).then(res2 => {
return {
currentPage: res2.page,
per_page: res2.per_page,
notices: res2.data,
total: res2.total,
totalPage: res2.total_pages
}
})
}
</script>
<script>
import { count } from '../../store.js'; // export const count = writable(1); from store.js file
export let currentPage; // 1
export let per_page; // 6
export let notices;
export let total; // 12
export let totalPage; // 2
const handleClick= () => {
if ($count < totalPage) {
count.update(n => n + 1); // update count 1 to 2 and want to deliver changed value to fetch new page
}
}
</script>
<main>
<div id="container">
<h1 class="cont-tit">Notice Board</h1>
<div class="noti-list-wrap">
{#each notices as notice, i}
<ul class="noti-list">
<li>
<a rel=prefetch href={`notice/${notice.id}`}>
<p class="tit">{`${notice.first_name} ${notice.last_name}`}</p>
<hr />
<p class="date">
{`notice no.${i}`}
</p>
</a>
</li>
</ul>
{/each}
<div class="show-more" on:click={handleClick}>show me more</div>
</div>
</div>
</main>
At first I thought I could use $count to fetch page 2 data but import count store inside script context="module" won't work.
Is there a way to deliver changed value of store to function preload?
Don't try to load the extra data inside preload. Instead, treat that as the initial data — which it is — and append to it the normal way.
This is a simplified version — it doesn't worry about error handling, race conditions, or an initial page other than 1 — to give a general idea of what I mean:
<script context="module">
export async function preload(page) {
const res = await this.fetch('https://reqres.in/api/users?page=1');
const data = await res.json();
return {
currentPage: data.page,
per_page: data.per_page,
notices: data.data,
total: data.total,
totalPage: data.total_pages
};
}
</script>
<script>
export let currentPage;
export let per_page;
export let notices;
export let total;
export let totalPage;
const load_more = async () => {
currentPage += 1;
const res = await fetch('https://reqres.in/api/users?page=' + currentPage);
const data = await res.json();
notices = notices.concat(data.data);
};
</script>
<!-- other stuff -->
{#if currentPage < totalPage}
<button on:click={load_more}>show me more</button>
{/if}
I usually use the shortcut also to update the store value, like so:
const handleClick= () => {
if ($count < totalPage) {
$count = $count + 1); // update count 1 to 2 and want to deliver changed value to fetch new page
}
}
I don't see the code where you actually fetch the nth page base on count.
Related
i'm having a trouble using the id from a template string item
const elementoParaInserirJogosNaLista = document.getElementById("listaJogos");
function exibirJogosNaTela(listaDeJogos) {
elementoParaInserirJogosNaLista.innerHTML = "";
listaDeJogos.forEach((jogo) => {
elementoParaInserirJogosNaLista.innerHTML += `
<div class="jogo">
<a href="paginajogo.html">
<img class="jogo__imagem" src="${jogo.imagem}" alt="${jogo.titulo}" />
</a>
<h2 class="jogo__titulo">${jogo.titulo}</h2>
<p class="jogo__preco" id="preco">R$${jogo.preco}<a ><img class="jogo__carrinho" id="addCarrinho" src="./images/addcart.png" alt="Adicionar ao carrinho"/></p><a/>
</div>
`;
});
}
i've tried to use the id "addCarrinho" and nothing happens
i'm newb on developing
const botoesAddCarrinho = [];
botoesAddCarrinho = document.querySelectorAll(".jogo__carrinho");
botoesAddCarrinho.forEach((evento) =>
evento.addEventListener("click", addNoCarrinho)
);
function addNoCarrinho () {
console.log('ok')
}
i've changed the selector to by the class, but nothings happens, is like the nothing was selected
i'm using the exibirNaTela on the fetch with the json
let jogos = [];
const endpointDaAPI ="jogos.json"
getBuscarJogosDaAPI();
async function getBuscarJogosDaAPI() {
const respost = await fetch(endpointDaAPI);
jogos = await respost.json();
exibirJogosNaTela(jogos.jogos);
}
Why when you are searching for something else is deleting the previous contents ?For example first you search for egg and show the contents but then when you search for beef the program deletes the egg and shows only beef.Thank you for your time code:
const searchBtn = document.getElementById('search-btn');
const mealList = document.getElementById('meal');
const mealDetailsContent = document.querySelector('.meal-details-content');
const recipeCloseBtn = document.getElementById('recipe-close-btn');
// event listeners
searchBtn.addEventListener('click', getMealList);
mealList.addEventListener('click', getMealRecipe);
recipeCloseBtn.addEventListener('click', () => {
mealDetailsContent.parentElement.classList.remove('showRecipe');
});
// get meal list that matches with the ingredients
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
let html = "";
if(data.meals){
data.meals.forEach(meal => {
html += `
<div class = "meal-item" data-id = "${meal.idMeal}">
<div class = "meal-img">
<img src = "${meal.strMealThumb}" alt = "food">
</div>
<div class = "meal-name">
<h3>${meal.strMeal}</h3>
Get Recipe
</div>
</div>
`;
});
mealList.classList.remove('notFound');
} else{
html = "Sorry, we didn't find any meal!";
mealList.classList.add('notFound');
}
mealList.innerHTML = html;
});
}
Beacuse you are using innerHTML , if you want to save the previous contents you should use append or innerHTML + = .
Because everytime you make a search, the html var is populated with new data.
if you move the 'html' variable to the root scope, this should get you there:
// get meal list that matches with the ingredients
let html = ""; // <-- place it outside the function body
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
// let html = ""; // <-- remove it from here
if(data.meals){
data.meals.forEach(meal => {
i have fetchTeam(teamName) function which gets an array of players and displays them in HTML. and another function that takes a player name as a parameter and displays the player stats. Something like this....
let result = document.getElementById("result");
let teamName;
const fetchTeam = async (teamName) => {
teamName = document.getElementById("teamName").value;
const response = await fetch(`http://localhost:3000/${teamName}`);
const data = await response.json();
let team = data.teamStats;
let players = data.playerStats;
const teamName = team[0].Name;
const logo = team[0].Logo;
const WL = team[0].WL;
result.innerHTML = `
<br><div class="top">
<h3>${teamName}</h3>
<h4>Win / Loss: ${WL}</h4>
<img src=${logo}></div>
<div class="flex-container">
<div class="flex-child">
<button class='name' onclick="fetchPlayer("${players[0][0].Player}")> ${players[0][0].Player} </button> ...
`
That all works fine except the onclick fetchPlayer function at the bottom.... Heres the code for that:
const results = document.getElementById("results");
const fetchPlayer = async (player) => {
const response = await fetch(`https://get-player.herokuapp.com/${player}`);
const data = await response.json();
results.innerHTML = `
<br>
<div class="layout">
<div class="child"><img src="${data.sprite}[0]"><br>${data.mons[0]}</div>
`
As you can see, i want to display the team members and then clicking on a member name will show their stats.
edit: format
There are several issues in your code.
First, you redeclare teamName in your fetchTeam function. That cannot work.
const fetchTeam = async (teamName) => {
teamName = document.getElementById("teamName").value;
...
const teamName = team[0].Name;
}
Second, your button syntax is incorrect :
<button class='name' onclick="fetchPlayer("${players[0][0].Player}")>
should be
<button class='name' onclick="fetchPlayer('${players[0][0].Player}')">
Third, as pointed out in this answer, in your fetchPlayer function
<img src="${data.sprite}[0]"> should be <img src="${data.sprite[0]}">
In the fetchPlayer function,
Change <img src="${data.sprite}[0]"> to <img src="${data.sprite[0]}">
I am basically looking to recreate this I have recorded on my phone:
However, one slight difference:
I want to max amount of selected items to be, for example, 5. If you have already checked 5 items and check another, it will replace array[0] with the one you just selected. If you select another, it will replace array[1] and so on.
In my case, Users have "medals" (basically like achievements in a game). They can choose 5 "medals" to display on their profile.
The window in which users can view all the medals they own and select them so far looks like this:
import React, { useState, useEffect } from "react";
//#####################################################################
import classNames from "classnames/bind";
//#####################################################################
import { useAuth } from "../../../../contexts/AuthContext";
import { useUser } from "../../../../contexts/UserContext";
//#####################################################################
import { db } from "../../../../firebase.js";
//#####################################################################
import "./ProfileEditMedalShowcase.scss";
//#####################################################################
function ProfileEditMedalShowcase() {
//-------------------------------------------------------
//STATES
//-------------------------------------------------------
//This stores data about the user (name, username, bio, profilePicture, ID's of medals unlocked so far)
const { userData } = useUser();
//This will store the array of all the medals that have been unlocked so far by the user.
const [userMedals, setUserMedals] = useState([]);
//This stores the medals the user wants to put on "showcase". These medals will show on their profile. (Ideally want to limit this to a length of 5).
const [selectedMedals, setSelectedMedals] = useState([]);
//-------------------------------------------------------
//USE EFFECTS
//-------------------------------------------------------
//When the component loads:
// 1) Set the user medals to an empty array, in case of refresh or re-render.
// 2) We already have an array of the medal id's that the user has unlocked. So we need to go get those medals data (image, medal title etc.)
// 3) The user already have some medals selected to be on show, so prefill the selected medals with them.
useEffect(() => {
if (userData) {
setUserMedals([]);
getUserMedalsData(userData.medals);
setSelectedMedals(userData.medalShowcase);
}
}, [userData]);
//-------------------------------------------------------
//METHODS
//-------------------------------------------------------
//Get the data of the medals that the user owns from firebase (medals have titles, images etc.).
async function getUserMedalsData(medals) {
const snapshot = await db.collection("medals").get();
snapshot.docs.forEach((medal) => {
if (medals.includes(medal.id)) {
setUserMedals((existingMedals) => [...existingMedals, medal.data()]);
}
});
}
//This is where I am currently trying to add/remove medals, not yet tackled the issue of overwriting if length is > 5.
function addRemoveSelectedMedal(medal) {
if (!selectedMedals.includes(medal)) {
const tempArray = selectedMedals;
tempArray.push(medal);
setSelectedMedals(tempArray);
} else {
const removedMedal = selectedMedals.filter(
(existingMedal) => existingMedal === medal
);
console.log("removed medal array: ", removedMedal);
setSelectedMedals(removedMedal);
}
}
//-------------------------------------------------------
//END
//-------------------------------------------------------
return (
<div className="profileEditMedalShowcase">
<h4>Choose Medals To Showcase</h4>
{/* Here we map through the medals that may already be selected to be highlighted and display them.*/}
<div className="profileEditMedalShowcase__selectedMedals">
{selectedMedals.length > 0 ? (
selectedMedals.map((medal) => {
<span
key={medal.medalTitle}
className="profileEditMedalShowcase__medalGridItem"
>
<img
src={medal.medalDownloadUrl}
className="profileEditMedalShowcase__medalGridItem__medal"
alt={medal.medalTitle}
/>
</span>;
})
) : (
<h4>No Medals Selected</h4>
)}
</div>
{/* Here we map through all the medals that the user has unlocked, so they can choose which ones they want to showcase.*/}
{userMedals.length > 0 && (
<div className="profileEditMedalShowcase__gridContainer">
{userMedals.map((medal, index) => (
<span
key={medal.medalTitle}
className="profileEditMedalShowcase__medalGridItem"
className={
selectedMedals.includes(medal)
? "profileEditMedalShowcase__medalGridItem medalSelected"
: "profileEditMedalShowcase__medalGridItem"
}
>
<img
src={medal.medalDownloadUrl}
className="profileEditMedalShowcase__medalGridItem__medal"
alt={medal.medalTitle}
/>
{/* This is a checkbox that will basically add or remove the medal from the showcase array.*/}
<input
type="checkbox"
name="showcaseMedals"
className="profileEditMedalShowcase__medalGridItem__checkbox"
value={medal}
onChange={() => {
addRemoveSelectedMedal(medal);
}}
/>
</span>
))}
</div>
)}
</div>
);
}
export default ProfileEditMedalShowcase;
So far it does not work. Plus I am confused over the logic on how to go about this. It seems quite involved and Im just wondering if somebody has the logic down?
TIA Guys!
I want to max amount of selected items to be, for example, 5. If you have already checked 5 items and check another, it will replace array[0] with the one you just selected. If you select another, it will replace array[1] and so on.
you're looking for a FIFO (first in, first out) array logic, here's an example of a function to accomplish this.
function addFifo(array, elementToAdd, maxCount) {
if (array.length >= maxCount) {
// remove first element of the array
array.shift();
}
// add element at the end of the array
array.push(elementToAdd);
}
if you don't want to alter your original array:
function addFifo(array, elementToAdd, maxCount) {
// clone array
const result = array.concat();
if (result.length >= maxCount) {
result.shift();
}
// add element to the end and return array
return result.concat(elementToAdd);
}
in your code you could do that:
function addRemoveSelectedMedal(medal) {
if (!selectedMedals.includes(medal)) {
const newSelection = selectedMedals.concat();
if (newSelection.length >= 5) {
newSelection.shift();
}
newSelection.push(medal);
setSelectedMedals(newSelection);
} else {
// ...
}
}
you can go further and wrap your function and logic in a useCallback + mutator function for the update:
const addRemoveSelectedMedal = React.useCallback(medal => {
setSelectedMedals(selectedMedals => {
if (!selectedMedals.includes(medal)) {
const newSelection = selectedMedals.concat();
if (newSelection.length >= 5) {
newSelection.shift();
}
newSelection.push(medal);
return newSelection;
} else {
return selectedMedals.filter(
existingMedal => existingMedal !== medal
);
}
});
}, []);
I have a simple notes app and delete isn't working properly, even though state is correctly updated.
The state is being updated correctly as I can see on the console.
But all the notes including and after that note that I click delete on are getting deleted on the DOM for some reason.
For example if I have 3 notes ["hello","hi","hey"], if I delete the second note("hi"), the state shows the correct notes ["hello","hey"] but both "hi" and "hey" are deleted on the page not just "hi" like it was supposed to.
I can't understand where I've gone wrong, so I'd like to correct it.
App.js:
handleDelete = (note_id) => {
const id = 'display-' + note_id;
console.log(id);
document.getElementById(id).style.display = 'none';//remove element
//delete the note and update state
const newNotesList = this.state.notesList;
newNotesList.splice(note_id,1);
this.setState({
notesList : newNotesList
})
console.log(this.state.notesList)
}
Display.js:
render(){
const notesList = this.props.notesList;
const displayNotes = notesList.map( (note,note_id) =>
<div id={ 'display-' + note_id } className="display">
{/*some code*/}
<button type="button" className="delete-button"
onClick = { () => this.props.handleDelete(note_id) } > Delete </button>
</div> );
return <div>{displayNotes}</div>;
}
do like this
// don't mutation object
// App.js
handleDelete = (note_id) => {
//delete the note and update state
const newNotesList = this.state.notesList.filter((item, index)=> index !== note_id)
this.setState({
notesList : newNotesList
})
}
// Display.js
render(){
const notesList = this.props.notesList;
const displayNotes = notesList.map( (note,note_id) =>
<div>
{/*some code*/}
<button type="button" className="delete-button"
onClick = { () => this.props.handleDelete(note_id) } > Delete </button>
</div> );
return <div>{displayNotes}</div>;
}
==== here is the reason ========
at first the state.note is ["hello","hi","hey"], in the function of handleDelete you delete "hi" and make the id of dispaly-1's display become to hidden, so when react render the state.note === ["hello","hey"] the element of "hey"'s id become dispaly-1 so "hey" will be hidden. you will only see "hello"
handleDelete = (note_id) => {
// this.state.notesList === ["hello","hi","hey"]
const id = 'display-' + note_id;
console.log(id);
// the problem is u hidden the next element
// 1. newNotesList.splice(note_id,1);
// 2. document.getElementById(id).style.display = 'none'
// two methods choose one or you will hidden two elements
document.getElementById(id).style.display = 'none';//remove element
//delete the note and update state
const newNotesList = this.state.notesList;
newNotesList.splice(note_id,1);
this.setState({
notesList : newNotesList
})
console.log(this.state.notesList)
// for example
// the `this.state.notesList` new is ["hello","hey"]
}
notesList.map( (note,note_id) =>
// `note` is ["hello","hey"] , u don't need hidden the `2rd` element
//you have been delete 'hi' ` the id of `display-1`'s display ==='hidden'
// now "hey"'s id is `display-1`
<div id={ 'display-' + note_id } className="display">
{/*some code*/}
<button type="button" className="delete-button"
onClick = { () => this.props.handleDelete(note_id) } > Delete </button>
</div> );
``