I got a bunch of objects from a JSON file I received from an API call, I need to render the message property of all the objects and in some of those objects there are hyperlinks within messages.
This is my HTML trying to make the hyperlink within the message clickeable
<div class="quote-text">
<i class="fas fa-quote-left"></i>
<span id="message"> </span>
</div>
This is my JS script which I cannot figure how to make it do what I want,
I believe that setting the attribute to the a tag when necessary iterating using for loop or forEach()
async function getQuote() {
loadingSpinner();
const API = 'https://tronalddump.io/random/quote';
try {
const response = await fetch(API);
const data = await response.json();
console.log(data.value)
messageText.innerText = data.value;
date.innerText = new Date(data.appeared_at).toDateString();
// authorText.innerText = data._embedded.author[0].name;
console.log(messageText)
if (data.value.length > 120) {
messageText.classList.add('long-quote');
} else {
messageText.classList.remove('long-quote');
}
completeSpinner();
} catch (error) {
// alert(error);
getQuote();
};
}
You can set the href property in the link.
Example:
var link = document.querySelector('.quote-text a');
link.href = data.url //Your JSON link property from the api.
Related
So, I'm fetching data from an API and using states. At the beginning, I don't get data from API and render the page with using the states with the data I wrote in the file. (I'm trying to say that I'm using static data at the first load). It works well. No problem at all. Then, when I get an input from user I connect with the API, do all the fetching and stuff and update the states. Everything seems normal. Data is received from API, states are updated, no error on console, first map function is rendered etc. but the second map function isn't. But surprisingly, when I change anything in the file and save it (you know that live server updates the page but doesn't reload), it applies the changes that I've done, and it also renders the second map function with using data I received earlier.
My first map function is this:
<div id="hourlyForecast">
{ weatherData.hourly.map((item, index) => {
/* ----- I'm getting a long string from API and these 2 lines are to get the part that i need from that string ------ */
let startFrom = item.dt_txt.length - 8;
let iNeed = item.dt_txt.toString().substr(startFrom, 5);
return (
<div className="hourly" key={index}>
<div className="hour">{iNeed}</div>
<div className="image">
<img
src={`/icons/${item.weather[0].icon}.png`}
alt="weather-icon"
/>
</div>
<div className="degrees">
{Math.round(item.main.temp)}°C
</div>
<div className="wind">
<img
src="wind.png"
alt="wind-icon"
style={{ transform: `rotate(${item.wind.deg}deg)` }}
/>{
{item.wind.speed} m/s
</div>
</div>
)
})
}
</div>
It is working, I guess. After that, I have this second map function:
<div id="daily">
{weatherData.daily.map((item, index) => {
return (
<div className="daily" key={index}>
<div className="day">
/* ------ Api gives me timestamp and this function returns me the day in human words :) ----- */
{giveMeDay(item.dt * 1000)}
</div>
<div className="dailyDegrees">
{Math.round(item.temp)}°C
</div>
<div className="dailyDesc">
{item.desc}
</div>
<div className="img">
<img src={`./icons/${item.icon}.png`} alt="weather-icon" />
</div>
</div>
);
})
}
</div>
It is also working, it should. I mean, they are not that complex.
So, all with that, here are what happens:
At first load I use static data, and it renders the second map function
(IMAGE) Rendered component with static data
When I enter an input it triggers the API Call, and it should re-render, but it does not (IMAGE) Empty Component even though API Call works
But when I change anything in the file after the API Call and save it, live server updates and the second map function is rendered. Let's say I change "°C" in the second map function with "°F" and save it. Then, everything works. (IMAGE) File updated without page being reloaded
I guess that's all I can say. I just don't understand the problem. Surely would appreciate any help.
Here is the part that I do the API stuff: (It can be a mess 'cause gotta do 3 API calls and didn't want to use async function due to my lack of experience with it)
var datam = {};
const work = (e) => {
e.preventDefault();
try {
fetch(
`${ApiUrl}weather?q=${e.target.cityName.value}&appid=${ApiKey}&units=metric&lang=en`
)
.then((data) => {
return data.json();
})
.then((gelen) => {
console.log(gelen);
if (gelen.cod === 200) {
datam = {
degrees: Math.round(gelen.main.temp),
description: gelen.weather[0].description,
feels_like: gelen.main.feels_like,
city: `${e.target.cityName.value}, ${gelen.sys.country}`,
min: gelen.main.temp_min,
max: gelen.main.temp_max,
icon: gelen.weather[0].icon,
lat: gelen.coord.lat,
lon: gelen.coord.lon,
};
} else {
alert("Couldn't get the data");
}
})
.then(() => {
console.log(datam);
fetch(
`${ApiUrl}forecast?lat=${datam.lat}&lon=${datam.lon}&units=metric&appid=${ApiKey}&lang=en`
)
.then((fivedays) => fivedays.json())
.then((veri) => {
console.log(veri);
datam.hourly = [];
if (veri.cod === "200") {
for (let i = 0; i < 8; i++) {
datam.hourly[i] = veri.list[i];
}
console.log(datam);
}
})
.then(() => {
datam.daily = [];
fetch(
`${ApiUrl}onecall?lat=${datam.lat}&lon=${datam.lon}&exclude=current,hourly,minutely,alerts&units=metric&appid=${ApiKey}&lang=en`
)
.then((donus) => donus.json())
.then((yanit) => {
console.log(yanit);
for (let i = 0; i < 6; i++) {
datam.daily[i] = {};
datam.daily[i]["temp"] = yanit.daily[i + 1].temp.day;
console.log("1");
datam.daily[i].desc =
yanit.daily[i + 1].weather[0].description;
datam.daily[i].icon = yanit.daily[i + 1].weather[0].icon;
datam.daily[i].dt = yanit.daily[i + 1].dt;
}
});
})
.then(() => {
console.log(datam);
// ------------ weatherData is the main state I'm using -----------
setWeatherData(datam);
// ------------ searched state is the important for only the first load. If they do the search i change what is being rendered -----------
setSearched(true);
});
//
});
} catch (error) {
alert(error);
}
};
The problem is this here: (VIDEO) Everything Works but doesn't appear until VS Code recompile
What I thought would be the easiest part of my project has turned into a Herculean effort. All I wanted to do was get data from a JSON file to then display on my website. Prior to using a JSON file, I hard coded some data to test my filter/search functionality, all of which I wrote in JavaScript. The code worked perfectly, so I decided to move the data to a JSON file as I am expecting to have a lot more data in the future and can't have it hardcoded. However, I have been unable to get data from the JSON file successfully. I tried using require('./data.json'), but apparently I can't just use require like that. I then tried importing the file, which only works if I go back to the html and add type="module" to the src tag. This then allows all of the data to display on the webpage, however, the function that allows me to filter by category no longer works. When I click on the buttons, I get no response. I used Inspect to get the console to find the error, and the output is:
Uncaught ReferenceError: filterProject is not defined
The search functionality still works, and I suspect this is because that code isn't inside a function. Thus, I don't know why filterProject is supposedly not defined when the other JS code works. Here is all of my code:
import projects from './data.json' assert { type: "json" };
const path = "http://localhost/static/images/";
//ADDING THE HTML, IGNORE
for(let i of projects){
let card = document.createElement("div");
card.classList.add("card", i["category"], "hide");
let imgContainer = document.createElement("div");
imgContainer.classList.add("image-container");
let imageOne = document.createElement("img");
imageOne.setAttribute("src", path.concat(i["imageOne"]));
imgContainer.appendChild(imageOne);
card.appendChild(imgContainer);
let container = document.createElement("div");
container.classList.add("container");
let name = document.createElement("h3");
name.classList.add("project-name");
name.innerText = i["projectName"].toUpperCase();
container.appendChild(name);
let student = document.createElement("h4");
student.classList.add("student-name");
student.innerText = i["studentName"].toUpperCase() + " mentored by " + i["mentor"].toUpperCase();
container.appendChild(student);
let category = document.createElement("h6");
category.innerText = i["category"].toUpperCase().replace("_", " ");
container.appendChild(category);
card.appendChild(container);
document.getElementById("projects").appendChild(card);
}
//FILTERING (DOESNT WORK)
function filterProject(value){
let buttons = document.querySelectorAll(".button-value");
buttons.forEach((button) => {
if(value.toUpperCase() == button.innerText.toUpperCase()){
button.classList.add("active");
}else{
button.classList.remove("active");
}
});
let elements = document.querySelectorAll(".card");
elements.forEach((element) => {
if(value == "all"){
element.classList.remove("hide");
}
else{
//having a space messes it up, make it _
if(element.classList.contains(value.replace(" ", "_"))){
element.classList.remove("hide");
}
else{
element.classList.add("hide");
}
}
});
}
//SEARCH (WORKS)
document.getElementById("search").addEventListener
("click", () => {
let searchInput = document.getElementById("search-input").value;
let elements = document.querySelectorAll(".student-name");
let cards = document.querySelectorAll(".card");
elements.forEach((element, index) =>{
if(element.innerText.includes(searchInput.toUpperCase())){
cards[index].classList.remove("hide");
}
else{
cards[index].classList.add("hide");
}
});
});
//INTIAL STATE
window.onload = () =>{
filterProject("all");
};
Here is the HTML just in case as well:
<div class ="wrapper">
<div id="search-container">
<input type="search" id="search-input" placeholder="Search student name here..."/>
<button id = "search">Search</button>
</div>
<div id ="buttons">
<button class = "button-value" onclick="filterProject('all')">All</button>
<button class = "button-value" onclick="filterProject('Creative Project')">Creative Project</button>
<button class = "button-value" onclick="filterProject('Developing Voice')">Developing Voice</button>
<button class = "button-value" onclick="filterProject('Interdisciplinary Fusion')">Interdisciplinary Fusion</button>
<button class = "button-value" onclick="filterProject('Personal Writing')">Personal Writing</button>
<button class = "button-value" onclick="filterProject('Curriculum Designer')">Curriculum Designer</button>
<button class = "button-value" onclick="filterProject('Internship')">Internship</button>
</div>
<div id = projects></div>
</div>
<script type = "module" src = "{{ url_for('static',filename='javascript/script.js') }}"></script>
If it matters, I am using Flask as my web framework. I'm not sure if that has any impact on anything, but it has created some obstacles when I've tried to create a live server to solve this issue. Thanks in advance for any replies!
What you're looking for is how to load json files locally.
One solution is
Start a local server e.g. http://localhost:8080
Then use fetch() to retrieve the json file
For example, if your data.json file was located within the same folder where you have your html file and where you started your server, then your code could be something like
fetch("http://localhost:8080/data.json")
.then((response) => {
return response.json();
})
.then((data) => {
// Add code to process your data
})
I'm working on a school task, but I recently got stuck with a textContent issue. I import a JSON file and use the data as a foreach. There are no errors in the .js file, but i receive a typeError: cannot set property 'textContent' of undefined, even though i defined the properties with elements from the JSON file?
When I remove the two lines with textContent, I receive a similar error with the appendChild property: cannot read property 'appendChild' of null.
If i log coffee.name in my forEach, I do get the correct first name. I'm guessing i only get one name since the forEach can't loop further because of the errors further along.
My js code:
import './style.css';
import data from './assets/data/coffees.json';
const init = () => {
console.log(data);
createPriceList(data);
};
const createPriceList = coffees => {
const ul = document.querySelector('prices__list');
console.log(coffees);
coffees.coffees.forEach(coffee => {
if (coffee.plantbased === true) {
const price = document.createElement('li');
price.classList.add('price');
const a = document.createElement('a').classList.add('price__button');
const spanWrapper = document.createElement('span').classList.add('price__button__wrapper');
const spanName = document.createElement('span').classList.add('price__button__name');
const spanAmount = document.createElement('span').classList.add('price__button__amount');
const spanPlus = document.createElement('span').classList.add('price__button__plus');
spanName.textContent = coffee.name;
spanAmount.textContent = coffee.prices.medium;
ul.appendChild(price);
price.appendChild(a);
a.appendChild(spanWrapper);
spanWrapper.appendChild(spanName);
spanWrapper.appendChild(spanAmount);
a.appendChild(spanPlus);
}
});
};
init();
Here is the HTML I'm trying to create (the section in comment, the rest is defined):
<section class="prices highlight spaced">
<h2 class="visually-hidden">Price list</h2>
<ul class="prices__list">
<!--<li class="price">
<a class="price__button">
<span class="price__button__wrapper">
<span class="price__button__name">Oat Latte</span>
<span class="price__button__amount">€ 2</span>
</span>
<span class="price__button__plus">+</span>
</a>
</li> -->
</ul>
</section>
You are trying to chain methods together like this:
const test = document.createElement('span').classList.add('price__button__name');
console.log(test.classList);
But, you have to create the element first and then you can work with its classList or other properties:
const test = document.createElement('span');
test.classList.add('price__button__name');
console.log(test.classList);
When user types in an input box and hit search button, I want to filter the data on the UI based i.e if the username starts with the entered text. I don't want to call the API again and again.
Using JavaScript Fetch API concept, I've tried to search by username but it is calling API on every search I made
This is what I've done
function searchData(){
let url = 'https://jsonplaceholder.typicode.com/users';
let data = document.getElementById("usersearch").value;
//passing the username, user enters as a url to the showData function
url = url+"?username="+`${data}`;
showData(url);
}
function showData(url){
fetch(url)
.then(response => response.json())
.then(data => {
let out = '<h2 class = "mt-3 mb-3">Search Result</h2>';
data.forEach(user =>{
out += `
<ul class = "mylist card">
<li id = "myli" class = "card-body text-primary pl-3"> ${user.name} </li>
<li class = "card-body text-secondary"> ${user.email} </li>
<li id = "myli2" class = "card-body text-info"> ${user.website} </li>
</ul>
`;
})
//Edit
document.getElementById("output").innerHTML = out;
})
.catch(err => console.log('Error : ',err.message))
}
//Edit
<button class = "btn btn-secondary" id = "btn1" onclick = "searchData()">Search</button>
I don't want to call the API again and again or every click of search button
Edits :
I am calling searchData() method using onclick function
Client-side filtering is never a good idea. As a general rule of thumb, you should prefer server-side filtering. Hitting the API guarantees correct results while client-side filtering may not. I have written a similar answer to this one.
Read here: which way is better?
You can declare two variables, one to store the entire user list and one to filter by your input and display.
Plus, it is more clear for you and the other devs when you seperate the fetch and the UI. So you must refactor showData() to seperate theses process.
So here the process =
Fetch all users and store them into a variable that will never change
To display them you use a second variable that will filter your result by your input
Each time you search , simply execute the searchData() function
Here is a functional snippet based on what i explained
let users = []; // user list
let usersFiltered = []; //user you display
// you wanna fetch ALL user without filter and store in your users variable
function fetchUsers() {
let url = 'https://jsonplaceholder.typicode.com/users';
fetch(url)
.then(response => response.json())
.then(data => {
users = data;
usersFiltered = users;
showData();
}).catch(err => console.log('Error : ', err.message));
}
// then this function only shows filtered users
function showData() {
let out = '<h2 class = "mt-3 mb-3">Search Result</h2>';
usersFiltered.forEach(user => {
out += `
<ul class = "mylist card">
<li id = "myli" class = "card-body text-primary pl-3"> ${user.name} </li>
<li class = "card-body text-secondary"> ${user.email} </li>
<li id = "myli2" class = "card-body text-info"> ${user.website} </li>
</ul>
`;
});
document.getElementById('users').innerHTML = out;
}
// the search data will filter your inputs
function searchData() {
let data = document.getElementById("usersearch").value;
usersFiltered = users.filter(user => {
// here i filter by name but you can implement what filter you want
return user.name.includes(data);
});
showData();
}
fetchUsers(); // to fetch users at the begening
<input id="usersearch" type='text' />
<button class = "btn btn-secondary" id = "btn1" onclick = "searchData()">Search</button>
<div id="users"></div>
I just cant seem to get the snapshot docs to show me the data from the server.
i have checked the collection. it is called "creaciones" without uppercase. I have 1 document and I have files written already. I've made no spelling mistakes whatsoever. I made this work before and now i cant.
db.collection('usuarios').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot.docs);
});
//Setup grilla
const setupGrilla = (data) => {
let html = '';
data.forEach(doc => {
const grilla = doc.data();
const creacion = `
<div>
<img src='jpg/${grilla.tipoCreacion}.png' alt='tipoCreacion'>
<h2>${grilla.nombreCreacion}</h2>
<img src='Imagenes/${grilla.nombreFoto}' alt='nombrefoto' class='imagen'>
<span>piezas: ${grilla.piezas}</span>
<span class='separador'></span>
<span>tiempo: ${grilla.tiempo} minutos</span>
<p>padre: ${grilla.ayuda} </p>
<p class='puntos'>Puntos: ${grilla.puntos} </p>
</div>
`;
html += creacion;
});
}
//get Data
db.collection('creaciones').get().then(snapshot => {
setupGrilla(snapshot.docs);
console.log(snapshot.docs);
});
I expect it to show fetch the database data.
db.collection('usuarios').get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot.docs);
});
That code is just what I have tried before. No need to look into that because I don't have it written at the moment.
You are calling setupGrilla with a snapshot.docs argument, but snapshot is never defined.
Try querySnapshot.docs instead, or rename querySnapshot in snapshot.
You are also passing the wrong argument to your method
db.collection('usuarios').get().then(function(snapshot) {
snapshot.forEach(function(doc){
console.log(doc.data);
});
setupGrilla(snapshot); // <-- Here
});