My HTML:
<body>
<form name="slectMovie">
IMDb rating:
<button type="submit" value="Select">Select</button>
</form>
<script src="script.js"></script>
</body>
My JS:
const getData = async () => {
try {
const res = await fetch("http://134.209.87.8:1337/api/movies");
const data = await res.json();
console.log(data);
const imdbSelect = document.createElement("select");
document.querySelector("form").append(imdbSelect);
if (data.data.length > 0) {
const IMDbRating = [
...new Set(data.data.map((x) => x.attributes.IMDbRating)),
];
IMDbRating.forEach((category) => {
const option = document.createElement("option");
option.textContent = category;
imdbSelect.append(option);
});
}
} catch (err) {
console.log(err);
}
};
getData();
I have one selection, that is movie rating, that I'm fetching from created API, and adding option values from API.
I would like to know how do I display items underneath form that are only selected in select tag.
For example if I chose first option, I would like to display items underneath form that only match that criteria, if I chose other option to display only itmes that have that option criteria.
there are many ways to achieve this. below is one of these.
In The html, add new line to display the items.
<form name="slectMovie">
IMDb rating:
<button type="submit" value="Select">Select</button>
</form>
<ul id="theList"></ul>
<script src="script.js"></script>
in javascript add few lines for display functionality
let data = [];
function dispplayData({ target }) {
const selectedItem = data.data.filter(
(d) => d.attributes.IMDbRating === Number(target.value)
);
const displayList = document.querySelector("#theList");
displayList.innerHTML = "";
selectedItem.forEach((item) => {
let listItem = document.createElement("li");
listItem.textContent = item.attributes.title;
displayList.appendChild(listItem);
});
console.log(selectedItem);
}
const getData = async () => {
try {
const res = await fetch("http://134.209.87.8:1337/api/movies");
data = await res.json();
console.log(data);
const imdbSelect = document.createElement("select");
document.querySelector("form").append(imdbSelect);
imdbSelect.addEventListener("change", dispplayData);
if (data.data.length > 0) {
const IMDbRating = [
...new Set(data.data.map((x) => x.attributes.IMDbRating)),
];
IMDbRating.forEach((category) => {
const option = document.createElement("option");
option.textContent = category;
imdbSelect.append(option);
});
}
} catch (err) {
console.log(err);
}
};
getData();
Hope this helps you.
Related
const getSearch = async function () {
try {
const res = await fetch(
`http://dataservice.accuweather.com/locations/v1/cities/search?apikey=${apiKey}&q=${inputValue}`
);
const data = await res.json();
// console.log(data);
const dataArr = Object.entries(data);
dataArr.forEach(([key, value]) => {
const city = value.AdministrativeArea.LocalizedName;
const country = value.Country.LocalizedName;
const countryCode = value.Country.ID;
const markup = `
<li class="search-result search-result--${key}">${city}, ${country}, ${countryCode}</li>
`;
console.log(markup);
});
ul.innerHTML = '';
ul.insertAdjacentHTML('afterbegin', markup);
} catch (err) {
console.error(err);
}
};
I am trying to add all li's to my ul. When I console.log(markup), the results come back separately, but when I insertAdjacentHTML() only the last li is displayed.
This is for search results, so the amount of li's is unknown for each search input, and I want the li's to match the number of results.
I'm surprised that it even worked once:
const getSearch = async function () {
try {
const res = await fetch(
`http://dataservice.accuweather.com/locations/v1/cities/search?apikey=${apiKey}&q=${inputValue}`
);
const data = await res.json();
// console.log(data);
const dataArr = Object.entries(data);
dataArr.forEach(([key, value]) => {
const city = value.AdministrativeArea.LocalizedName;
const country = value.Country.LocalizedName;
const countryCode = value.Country.ID;
const markup = `
<li class="search-result search-result--${key}">${city}, ${country}, ${countryCode}</li>
`;
console.log(markup);
// try this
ul.insertAdjacentHTML('afterbegin', markup);
});
} catch (err) {
console.error(err);
}
};
I would rather use createElement than innerHTML
I am working on a project using the Poke API.
My problem is when trying to filter the data from a search input,
I get the error: Cannot read properties of undefined (reading 'map')
When someone types in the search bar, I'm using filter to return a new array that matches either a name, id number, or type. I can see that a new array is returned, but the program breaks when trying to display the filtered data on the page.
It looks like the error occurs because the filter cannot iterate over a promise used to map specific data from the original array. But I'm not sure how to move forward.
I'm grateful to anyone who can point me in the right direction. Thanks for your help.
JS
const searchBar = document.getElementById('searchBar');
const pokemonData = [];
const getPokemonData = async () => {
for (let i = 1; i <= 151; i++) {
const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
const res = await fetch(url);
const data = await res.json();
pokemonData.push(data);
}
Promise.all(pokemonData).then( (results) => {
const pokemon = results.map( (data) => ({
name: data.name,
id: data.id,
image: data.sprites['front_shiny'],
type: data.types.map((type) => type.type.name)
}));
//console.log(pokemon);
displayPokemon(pokemon);
});
}
const displayPokemon = (pokemon) => {
//console.log(pokemon);
const pokeDexContainer = document.querySelector('.pokedex');
const generateHtml = (pokemon).map( (mon) => {
return `
<li class="poke-card">
<image class="poke-image" src="${mon.image}" alt="${mon.name}"/>
${
( ids => {
if (ids < 10) {
return `<h2 class="poke-id">00${ids}</h2>`
}
if (ids >= 10 && ids < 100) {
return `<h2 class="poke-id">0${ids}</h2>`
}
if (ids >= 100) {
return `<h2 class="poke-id">${ids}</h2>`
}
})(mon.id)
}
<h1 class="poke-name">${mon.name}</h1>
//error message points here
//Cannot read properties of undefined (reading 'map')
${mon.type.map( (types) => {
return `<span class="poke-type ${types}">${types}</span>`
} ).join('')}
</li>
`
}).join('');
pokeDexContainer.innerHTML = generateHtml;
}
getPokemonData();
searchBar.addEventListener('keyup', (event) => {
//console.log(event.target.value);
const searchString = event.target.value.toLowerCase();
const searchNumber = event.target.value;
const filteredPokemon = pokemonData.filter( (mon) => {
return (
mon.name.toLowerCase().includes(searchString) ||
mon.id == searchNumber ||
mon.type == searchString
);
});
console.log(filteredPokemon);
displayPokemon(filteredPokemon);
});
HTML
<!-- search bar -->
<div id="search">
<input type="text" name="searchBar" id="searchBar" placeholder="search pokedex"/>
</div>
<!-- pokemon list container -->
<ul class="pokedex"></ul>
I figured it out, in case this may be helpful to someone else.
The displayPokemon function would break when passing in filteredPokemon. filteredPokemon was iterating over the pokemonData array which holds the raw data from the Poke API.
Instead filteredPokemon needs to iterate over the pokemon map within the getPokemonData function, which is how displayPokemon displays the correct data.
So, we push the pokemon map to a new array using the spread operator and iterate over new array in filteredPokemon.
let pokemonResults = [];
const getPokemonData = async () => {
let pokemonData = [];
for (let i = 1; i <= 151; i++) {
const url = `https://pokeapi.co/api/v2/pokemon/${i}`;
const res = await fetch(url);
const data = await res.json();
pokemonData.push(data);
}
Promise.all(pokemonData).then( (results) => {
const pokemon = results.map( (data) => ({
name: data.name,
id: data.id,
image: data.sprites['front_shiny'],
type: data.types.map((type) => type.type.name)
}));
pokemonResults.push(...pokemon);
displayPokemon(pokemon);
});
}
Note: Since pokemon types returns an array, it needs to be converted to a string in order to get the correct search results.
searchBar.addEventListener('keyup', (event) => {
const searchString = event.target.value.toLowerCase();
const searchNumber = event.target.value;
const filteredPokemon = pokemonResults.filter( (mon) => {
return (
mon.name.toLowerCase().includes(searchString) ||
mon.id == searchNumber ||
JSON.stringify(mon.type).toLowerCase().includes(searchString)
);
});
displayPokemon(filteredPokemon);
});
Right now I'm working on full stack application that uses JS on the front and back end. This app lets a user generate their own set of flash cards. Whenever the user clicks "View Cards" data will then be fetched and will display the question and answer on each side of the card. It's supposed to display one card at a time and allows the user to scroll through other cards using either the "Previous" or "Next" buttons. I'm able to successfully fetch data convert it to JSON and can get at least one item from the data base display properly. The problem is whenever I try to scroll through the cards. I'm only able to scroll through some of the cards before the browser returns an error. I've even noticed that some of the cards won't render both sides properly. How could I address these issues?
const flashCard = document.querySelector(".flashcard");
const flipBtn = document.querySelector(".flip-btn");
const prevBtn = document.querySelector(".prev-btn");
const nextBtn = document.querySelector(".next-btn");
let frontOfCard = document.querySelector(".front-content");
let backOfCard = document.querySelector(".back-content");
const displayCards = () => {
getCardInfo()
flipBtn.innerHTML = "Flip"
flipBtn.removeEventListener("click", displayCards)
}
flipBtn.addEventListener("click", displayCards)
const flash = () => {
if (flashCard.style.transform != "rotateX(180deg)") {
flashCard.style.transform = "rotateX(180deg)"
} else {
flashCard.style.transform = "none"
}
}
const getCardInfo = async () => {
const itemBody = {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
}
const data = await fetch(window.location.href, itemBody)
const jsonData = await data.json()
console.log(jsonData)
let idx = 0;
frontOfCard.innerHTML = jsonData[idx].Answer
backOfCard.innerHTML = jsonData[idx].Question
flashCard.style.display = "block";
flipBtn.addEventListener("click", flash);
scrollThroughCards(idx, jsonData);
}
function scrollThroughCards(idx, data) {
prevBtn.addEventListener("click", (e) => {
flashCard.style.display = "none"
setTimeout(() => {
frontOfCard.innerHTML = data[idx--].Answer
backOfCard.innerHTML = data[idx--].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
nextBtn.addEventListener("click", (e) => {
flashCard.style.display = "none"
setTimeout(() => {
frontOfCard.innerHTML = data[idx++].Answer
backOfCard.innerHTML = data[idx++].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
}
app.get("/card/:id", checkAuthenticated, async (req,res) => {
const { id } = req.params
const data = await Card.findAll({ where: { NoteId: id } });
res.render("cards-page", {
noteid: id,
Cards: data
})
});
app.put("/card/:id", checkAuthenticated, async (req,res) => {
const { id } = req.params
const data = await Card.findAll({ where: { NoteId: id } });
res.json(data)
})
app.post("/card/:id", checkAuthenticated, async (req, res) => {
const { Question, Answer, NoteId } = req.body;
const newCard = await Card.create({
Question,
Answer,
NoteId
});
res.redirect(`/card/${NoteId}`)
});
In the scrollThroughCards function, boundary checks were not performed and the increment and decrement operators were misused.
function scrollThroughCards(idx, data) {
prevBtn.addEventListener("click", (e) => {
// there's no more card on the left of index 0
// so exit the function early
if (idx <= 0) return;
flashCard.style.display = "none"
setTimeout(() => {
idx--; // decrease the index first
// then use the modified index
frontOfCard.innerHTML = data[idx].Answer
backOfCard.innerHTML = data[idx].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
nextBtn.addEventListener("click", (e) => {
// there's no more cards beyond the end of the list
// so exit the function early
if (idx >= data.length - 1) return;
flashCard.style.display = "none"
setTimeout(() => {
idx++; // increase the index first
// then use the modified index next
frontOfCard.innerHTML = data[idx].Answer
backOfCard.innerHTML = data[idx].Question
flashCard.style.display = "block"
}, 1000)
e.preventDefault()
})
}
i'm new on react hooks(typescript), here i'm having trouble figuring out how to filter by two, at the moment search filtering is working with one filter and it is 'receiver?.name?'
i want to add one more filter and it is 'sending?name?'
here is working code:
const handleSearchh = (searchText: string) => {
const filteredEvents = orders?.filter(({ receiver }) => {
const name = receiver?.name?.toLowerCase() ?? "";
return name.includes(searchText);
});
setData(filteredEvents);
};
<Searchh onSearch={handleSearchh} />
<Table
dataSource={Data}
columns={columns}
/>
i want to add this to it
const filteredEvents = orders?.filter(({ sending }) => {
const name = sending?.name?.toLowerCase() ?? "";
return name.includes(searchText);
});
it is antd table i want it to filter the search by two parameters , receivers and senders name
You can try this solution.
const handleSearchh = (searchText: string) => {
filterData(searchText)
};
const filterData = (searchText) => {
const filteredEvents = orders?.filter(({ receiver, sending }) => {
const receiverName = receiver?.name?.toLowerCase() ?? "";
const sendingName = sending?.name?.toLowerCase() ?? "";
return receiverName.includes(searchText) && sendingName.includes(searchText);
});
setData(filteredEvents);
}
This isn't antd specific, but can't you just chain the filter functions?
// ...
const filterFuncOne = ({ receiver }) => {
const name = receiver?.name?.toLowerCase() ?? "";
return name.includes(searchText);
};
const filterFuncTwo = ({ sending }) => {
const name = sending?.name?.toLowerCase() ?? "";
return name.includes(searchText);
}
const filteredEvents = orders?
.filter.(filterFuncOne)
.filter(filterFuncTwo); // <-- chained
//...
am working on a pagination using Firebase and so far i have a button to go forward and other one to get back and they are working fine ,but i have problem detecting either if am in the first page or the last page so i can disable the pagination buttons,so am wondering how that work and should i change the way i paginate data?
export const getNextItems = (last_Visible) => {
return async (dispatch, getState, { getFirebase }) => {
const firestore = getFirebase().firestore();
// const items = [];
const dbRef = firestore
.collection('items')
.orderBy('createdAt', 'desc')
.startAfter(last_Visible)
.limit(2);
const usersRef = firestore.collection('users');
let temps = [];
const { data: items, firstVisible, lastVisible } = await dbRef.get().then(getAllDocs);
for (const item of items) {
const { data: user } = await usersRef.doc(item.owner).get().then(getDoc);
temps.push({ ...item, owner: user });
}
return { docs: temps, lastVisible, firstVisible };
};
};
export const getPrevItems = (first_Visible) => {
return async (dispatch, getState, { getFirebase }) => {
const firestore = getFirebase().firestore();
// const items = [];
const dbRef = firestore
.collection('items')
.orderBy('createdAt', 'desc')
.endBefore(first_Visible)
.limitToLast(2);
const usersRef = firestore.collection('users');
let temps = [];
const { data: items, lastVisible, firstVisible } = await dbRef.get().then(getAllDocs);
for (const item of items) {
const { data: user } = await usersRef.doc(item.owner).get().then(getDoc);
temps.push({ ...item, owner: user });
}
return { docs: temps, lastVisible, firstVisible };
};
};
To detect whether there are more pages to load, you'll need to request an additional item. So since you seem to show 2 items per page, you should request 3 items - and display only two of them. If you get a third item, you know there's an additional page.