Dynamically update html content plain JS - javascript

So I have a script bound in my index.html, which literally has to fetch an array of jsons from an api, then dynamically update my page when get response.
But as a result I'm getting an as the only update of my page.
here is my script
fetch(url)
.then((resp) => resp.json())
.then((resp) => {
resp.results.forEach(item => {
fetch(item.url)
.then((response)=> response.json())
.then((response) => pokeArr.push(response))
.catch(console.log)
})
}).catch(console.log)
const filler = () => {
if(!pokeArr) return 0
pokeArr.map((i,j)=>{
return `<tr>${i.name}</tr>`
})
}
const pokeindex = () => {
document.getElementById('a').appendChild(document.createElement(filler()))
}
pokeindex()
When I'm consoling it, I can see in the console all the responses I get, so I'm at least doing the fetching part right.

There are a number of issues here:
You have parberakan in one place but pokeArr in another; in a comment you've suggested they're meant to be the same thing.
You don't declare parberakan/pokeArr anywhere in the quoted code.
Nothing in the quoted code is waiting for the various fetch calls to complete (not even the first, and esp. not the series of them in one of its then handlers).
The fetch calls aren't checking .ok; see the post on my anemic little blog.
document.createElement doesn't accept HTML strings. It accepts the tag name of the single element to create (e.g., document.createElement("div")).
filler doesn't return the result of map.
Passing console.log directly to catch isn't 100% reliable.
At a rough guess, I suspect you want to do something like this (see comments), but I do recommend stepping back from your current task and working through some basic tutorials:
// Create these functions first
const filler = (pokeArray) => { // Accept the array as a parameter
if(!pokeArray) return null; // Recommend null instead of 0 if the other return is an array
return pokeArray.map((i,j)=>{ // Return the result of `map`
return `<tr>${i.name}</tr>`;
});
};
const pokeindex = (pokeArray) => { // Accept the array as a parameter
// I'm assuming the element with id="a" is a `table` or `tbody`
document.getElementById('a').insertAdjacentHTML( // This accepts HTML
"beforeend",
fillter(pokeArray)
);
}
fetch(url)
.then((resp) => {
// Check .ok
if (!resp.ok) {
throw new Error("HTTP error " + resp.status);
}
return resp;
})
.then((resp) => resp.json())
.then((resp) => Promise.all(resp.results.map(item => { // Wait for all these to complete;
// result is an array of the
// resolution values
return fetch(item.url)
.then((response) => {
// Check .ok
if (!response.ok) {
throw new Error("HTTP error " + resp.status);
}
return response;
})
.then((response) => response.json());
})))
.then((pokeArray) => {
// Now we have the array and can do something
pokeindex(pokeArray);
})
.catch(error => { console.log(error); }); // Passing `console.log` directly isn't reliable
I may not have caught every last little thing, though. This is just to help get you pointed the right way.

Related

Nested fetch() functions with conditional if statement in JavaScript

I have a function that runs a fetch() and at the very end of the fetch call it logs to the console.
I want to conditionally add a second fetch function before the end of the first. It also logs to the console.
I'm having trouble working out how to ensure the first log waits for the second fetch to complete and log its data before actually logging (whether or not it actually needs to run). I've been able to do this with several else statements, but I'd like to avoid the repetition.
Codepen # https://codepen.io/cfxd/pen/bGKmRZj
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();
Use await
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(async resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
await fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();
or even better
function alwaysRuns() {
fetch('https://api.spacexdata.com/v4/launches/latest')
.then(result => result.json())
.then(resultJson => {
const rocketId = resultJson.rocket;
const rocketName = resultJson.name;
if(rocketName.includes('Crew')) {
return fetch(`https://api.spacexdata.com/v4/rockets/${rocketId}`)
.then(rocketResponse => rocketResponse.json())
.then(rocketJson => {
console.log('This fetch might not happen, depending on the conditional')
});
}
}).then(() => {
console.log('this should always log last whether the conditional causes the second fetch to run or not!');
});
}
alwaysRuns();

Returning data from a Fetch api to build a UI [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
First time using Stack Overflow, sorry if I do something wrong.
I'm building a simple app that allows you to enter a characters name from Rick and Morty, and then build a small card that displays that characters name, a pic of the char, their status, and their current location.
I currently can get the input from the input field and send that data to the API no problem. my issue is getting the data back from the API and setting it to a variable so I can then extract each individual piece of info from the API. i.e. character.name , character.location, etc...
Here's my JavaScript so far:
// UI variables
const input = document.getElementById("searchChar");
// Event Listeners
input.addEventListener("keyup", (e) => {
const userText = e.target.value;
if (userText !== "") {
let response = fetch(
`https://rickandmortyapi.com/api/character/?name=${userText}`
)
.then((res) => res.json())
.then((data) => console.log(data));
}
});
I can't return the 'data' or set it to a variable from the .then(data), so how do I get it to use it elsewhere in my code?
Any help is appreciated. I'm new to the Fetch API. Thank you so much!
Updated the answer based on below suggestions in comments.
Many people use this: which is an anti-pattern, What is the explicit promise construction antipattern and how do I avoid it?
Not to Use:
You can wrap your fetch call in a promise and return as a promise.
as an example
function testfunction(arg1, arg2) {
return new Promise((resolve, reject) => {
fetch(apiPath)
.then(resp => {
if (resp.ok) return resp.json()
})
.then(resp => resolve(resp)).catch(err => {
reject(err)
})
})
}
Calling the same from anywhere from your application
testfunction(arg1, arg2)
.then(result=> {
// here you can use the API result
})
.catch(error => {
throw error;
});
This is how you can call your fetch API method and use it at all the places:
function testfunction(arg1, arg2) {
const request = baseApi.InitialiseGetRequest();
return fetch(apiPath).then(resp => {
if (resp.ok) return resp.json()
})
.then(resp => resolve(resp)).catch(err => {
reject(err)
})
}
This is how you can use it accross the application:
testfunction(arg1, arg2)
.then(result=> {
// here you can use the API result
})
.catch(error => {
throw error;
});

Using Fetch in React to fetch data from server

I am trying to fetch data from the server(Node.js) with the following code:
componentDidMount = () => {
fetch('http://localhost:3000/numberofjobs')
.then(response => response.json())
.then(numberOfJobs => {
console.log(numberOfJobs)
})
}
That's my route in Node:
const handleNumberOfJobs = (req, res, Register) => {
Register.find({})
.then(users => {
const total = users.reduce((sum, { numberOfJobs }) => sum +
numberOfJobs, 0);
console.log(total);
})
.then(response => res.json(response))
}
One problem I'm having is the Front-end console.log is not showing up in the console, and I don't know why. In the server side, when the pages loads it does console.log the sum and everything, so it's working so I believe I am doing something wrong with React. I want to bring this info to my front-end so I could display it on the page.
Thanks a lot!!
TL;DR
There's a mistake in how you use arrow functions' implicit returns in your server side code.
The fix is to just add a return total; in the first .then(...) handler.
Details
First off, let's get it out: I agree with the comments on not neglecting error checks! (Be it fetch or anything else.)
Anyway: You use arrow functions in your .then(...) handlers. But that last statement in the first one is console.log(total). The return value of that call is undefined, which becomes the implicit return value of your arrow function. The promise then passes this on as the value of response in your second .then(...) handler. (You could verify that by adding console.log(response) in the second .then(...) handler.
The fix is to just add a return total; in the first .then(...) handler:
const handleNumberOfJobs = (req, res, Register) => {
Register
.find({})
.then(users => {
const total = users.reduce(
(sum, { numberOfJobs }) => sum +
numberOfJobs, 0
);
console.log(total); // <-- returns undefined
return total; // <-- pass on to next promise
})
.then(response => {
// console.log(response); // if you're curious
res.json(response)
})
}
}
Personal hint: Indent / lint your code for easier maintenance.

Extracting array elements from JSON (javascript)

I'm trying to manipulate JSON data received from an API url (this is my first time handling this type of work)
The following function returns a promise of a 20 element array:
const articles = () => {
return fetch(url)
.then(res => res.json())
.then(post => post.articles);
};
Console view:
Now, I'd like to extract the elements from the array - I tried something like:
articles()[0].name
but this doesn't work and I'm not sure of an alternative way to go about this? Appreciate your help. Thanks
Your articles fucntion returns a promise. You have to consume the promise (more on MDN):
articles().then(articleArray => {
console.log(articleArray);
});
or within an async function:
const articleArray = await articles();
console.log(articleArray);
Side note: Your fetch code is missing a check for HTTP success (HTTP failure isn't a rejection). You're by far not the only person who misses out this check, so much so that I've written a post on my anemic blog about it. With the check:
const articles = () => {
return fetch(url)
.then(res => {
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.json();
})
.then(post => post.articles);
};

fetch response.json() and response.status

Is this the only way to use the body.json() and also get the status code?
let status;
return fetch(url)
.then((response => {
status = response.status;
return response.json()
})
.then(response => {
return {
response: response,
status: status
}
});
This doesn't work as it returns a promise in the response field:
.then((response)=> {return {response: response.json(), status: response.status}})
Your status is not visible in the second then. You can just get the two properties in the single then.
json() returns a new Promise to you, so you need to create your object inside the then of the result of that function. If you return a Promise from a function, it will be fulfilled and will return the result of the fulfillment - in our case the object.
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(r => r.json().then(data => ({status: r.status, body: data})))
.then(obj => console.log(obj));
The .json method returns a promise, not the parsed value itself. If you want to access both the response and the parsed value in the same callback, you'll need to use nested functions like this:
fetch(url)
.then(response => {
response.json().then(parsedValue => {
// code that can access both here
})
});
Alternatively, you can use await inside an asynchronous function to eliminate the need for callbacks.
const response = await fetch(url);
const parsedValue = await response.json();
// code that can access both here
Of course, you'll want to check for errors, either with a .catch(...) call on a Promise or with a try...catch block in an async function. You could make a function that handles JSON and error cases, and then reuse it for all fetches. For example, something like this:
function handle(response) {
if (response.ok) {
return response.json().then(parsedValue => {
// the status was ok and the body could be parsed
return Promise.resolve({ response, parsedValue });
}).catch(err => {
// the status was ok but the body was empty or not JSON
return Promise.resolve({ response });
});
} else {
return response.json().catch(err => {
// the status was not ok and the body was unobtainable/empty/not JSON
throw new Error(response.statusText);
}).then(parsedValue => {
// the status was not ok and the body was JSON
throw new Error(parsedValue.error.message); // assuming an error message is returned by our REST API
});
}
}
I don't think it's the best design pattern, but hopefully this clarifies how the fetch API works.
PS: I avoided naming any variable or property json since that is the name of the text format. Once it's been parsed, it is no longer JSON.
Using two 'then's seem unnecessary to me.
async/await could get the job done pretty easily.
fetch('http://test.com/getData')
.then( async (response) => {
// get json response here
let data = await response.json();
if(data.status === 200){
// Process data here
}else{
// Rest of status codes (400,500,303), can be handled here appropriately
}
})
.catch((err) => {
console.log(err);
})
Did you try this?
return fetch(url)
.then((r)=> {return {response: r.json(), status: r.status}})
I think the cleanest way is to create a Promise.all() with the pieces you need.
.then(response => Promise.all([Promise.resolve(response.ok), response.text()]))
Which can be written shorter as
.then(response => Promise.all([response.ok, response.text()]))
The promise returns an array with all of the results
.then(data => ({ status: data[0], response: data[1] }))

Categories