response.text() does not contain what i expect - html

I have a mocked file mockedfile.html which I am redirecting to locally. My problem is that when I try to extract the HTML from the fetch response, the result does not look exactly like the mocked file. So this is what I do:
fetch('/some-url.html').then(response => {
if (response.redirected) { // this will be true locally
fetch(response.url) // which will then be /mockedfile.html
.then(res => res && res.text())
.then(text => text) // expect this text to be the same as the content inside mockedfile.html, but this is just partly the same
} else {
response && response.text().then(text => text);
}
}
})
What am I missing? Is there a more propper way to solve this?

To get the values outside of your fetch, you need the first .then() return something.
The extra .then() is unnecessary, because it just creates a new promise resolving to the same value.
fetch('/some-url.html')
.then(response => {
if (response.redirected) {
return fetch(response.url)
.then(res => res.text());
}
return response.text();
})
.then(console.log)
.catch(console.error)

You have to add ) to close brace opened at then(
fetch('/some-url.html').then(response => {
if (response.redirected) { // this will be true locally
fetch(response.url) // which will then be /mockedfile.html
.then(res => res && res.text())
.then(text => text) // expect this text to be the same as the content inside mockedfile.html, but this is just partly the same
} else {
response && response.text().then(text => text);
}
});
})

fetch(url).then(result => result.text()).then(yourText => ...);

Related

Why is this promise returning an [object Promise] and not the value?

I thought I fully understood promises, but I'm stumped on this. I realize I should use async/await, but for this example I specifically want to only use .then().
When I do this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json() )
.then( data => {
console.log(data)
return data
});
the console.log(data) in the last function prints the JSON as expected, but when I try to console.log(theJson), the returned value, it prints [object Promise].. Why is this?
I was able to get the data outside of the function using react's useState/useEffect but not with just a vanilla global variable. I'm not trying to solve anything, but just want to understand why this does not work.
export default function App() {
let globalVar;
const [theQuote, setTheQuote] = useState({});
useEffect(() => {
fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
.then(quoteTypeResponse => quoteTypeResponse.json())
.then(quoteType =>
fetch(
'https://programming-quotes-api.herokuapp.com/quotes/' +
quoteType.type
)
)
.then(quoteResponse => {
return quoteResponse.json();
})
.then(quote => {
setTheQuote({ quote: quote.en, author: quote.author });
globalVar = quote.author;
});
}, []);
return (
<div id="app">
<h1>{theQuote.quote}</h1> // renders
<h2>{theQuote.author}</h2> // renders
<h3>globalVar: {globalVar}</h3> // undefined
</div>
);
}
Because your second .then() is inside the first then(), so theJson is a Promise<T>. The nice thing about Promise<T> is that you can move an inner .then() call up a level and it will still work:
Change it from this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json().then( data => {
console.log(data)
return data
} )
);
To this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json() )
.then( data => {
console.log(data)
return data
});
But ideally, use async function so you can have this considerably simpler code instead:
const resp = await fetch( `https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json` );
const data = await resp.json();
console.log( data );
#pushkin left a good link explaining the differences between async/await and using .then(), but basically, the value returned by the then() is only available within that block.
Promises cheat sheet: https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f
fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
.then(quoteTypeResponse => quoteTypeResponse.json())
.then(quoteType =>
fetch(
'https://programming-quotes-api.herokuapp.com/quotes/' + quoteType.type
)
)
.then(quoteResponse => {
return quoteResponse.json();
})
.then(quote => {
console.log(`q:${util.inspect(quote)}`);
document.getElementById('app').innerHTML = quote.en;
});

Return json data from Function

I use a function for Fetch with below code :
var URL='...'
export function PostData(method,data){
fetch(URL+method,{
method:'POST',
body:JSON.stringify(data),
headers:{'Content-Type':'application/json'},
}).then(res => res.json())
.then(response => {
var ret=JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
and use it like below :
var retData=PostData('login/Authenticate',data)
retData is empty but in function ret has data
You PostData function does currently not return anything, so it is empty.
First step would be to add a return statement:
export function PostData(method,data){
return fetch(URL+method,{
method:'POST',
...
This will make your function return a value, but not just a simple value, but a promise! Promises are not the easiest to understand, but there is also a of people who tried to explain them
- https://developers.google.com/web/fundamentals/primers/promises
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Now how can you use the value anyway?
PostData('login/Authenticate',data)
.then(retData => {
// ... use retData here
});
Now, you used the react-native tag, so I am assuming you want to use this value in your render function. You can't do this simply by putting the PostData call in your render function. You'll have to put it in state, and then use that value in render:
state = { retData: null }
componentDidMount() {
PostData('login/Authenticate',data)
.then(retData => {
// This puts the data in the state after the request is done
this.setState({ retData: retData });
});
}
render() {
let retData = this.state.retData;
// ... use retData in your render here, will be `null` by default
There are a lot more different or cleaner ways to do this, but I tried to keep this answer as simple and bare as possible :)
It is empty at this point because the call to fetch is asynchronous and the literal is set to undefined as it moves to the next statement because it has not been resolved yet. One way around it is to return the promise object itself and then use .then to get the response once it is resolved.
var URL = '...'
export function PostData(method, data) {
// return the promise object
return fetch(URL + method, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
}).then(res => res.json())
.then(response => {
var ret = JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
PostData('login/Authenticate',data).then(response => {
// do something with the response
});
A cleaner approach would be is to use the async/await ES7 feature which makes it more readable.
var URL = '...'
export function PostData(method, data) {
// return the promise object
return fetch(URL + method, {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
},
}).then(res => res.json())
.then(response => {
var ret = JSON.stringify(response)
return ret
})
.catch((error) => {
console.error(error)
})
}
async function getData() {
let retData = await PostData('login/Authenticate',data);
}

How to render 2 json's in JSX

I have 2 json's which I get on making 2 different API calls.
fetch(`veb/api/roleDetails/${id}`).then(response =>
response.json()
).then(responseJson => {
console.log('responseJson = ' + JSON.stringify(responseJson));
this.setState(() => {
return {
allRoleDetails: responseJson,
}
});
}
).catch(error => console.error('Error:', error))
2nd:
fetch(`/api/api/itemTypes`).then(response =>
response.json()
).then(responseJson => {
console.log('responseJson = ' + JSON.stringify(responseJson));
this.setState(() => {
return {
names: responseJson,
}
});
}
).catch(error => console.error('Error:', error))
in the 1st api call I get itemtypeid from which I have to make the 2nd api call to fetch the name of the typeid.Even if I get a combined json it will be fine
How to do that?Any help will be great.
Thanks
Promise.all is what you need. Basically, what it does is merging a collection of promises into one single promise, and return the resolved values one everything inside the collection done.
Promise.all([
fetch(`veb/api/roleDetails/${id}`).then(response => response.json()),
fetch(`/api/api/itemTypes`).then(response => response.json())
]).then(([allRoleDetails, names]) => {
// allRoleDetails is the resolve of your first promise
// names is the resolve of your second promise
}).catch(err => {
// catch error here
});
More details about Promise.all can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

how to access nested data by nesting fetch calls?

I'm having trouble understanding the best approach to this.
My goal is to display nested data.
I use fetch on this url - https://horizons-json-cors.s3.amazonaws.com/products.json
which takes me to a page that contains json. inside the json is 3 urls. each url contains the data that I need to get to.
So far, I've accessed the first layer, and now have an array of the item urls. I guess I don't understand how to fetch the data while im inside the outter fetch call.
Here's my code thus far (the result is an array of urls, where each url contains the data I need.) :
componentDidMount() {
console.log('Fetch');
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
var productUrlArr = [];
for (var i = 0; i < json.length; i++) {
productUrlArr.push(json[i].url);
}
.catch((err) => {
console.log('error', err);
});
}
If you guys could help me out and really walk through how to access the next level of data, I would really, really appreciate it.
You can Fetch Data for Inner URLs this way too,
// 1. Outer Fetch call initiated here
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then(res => {
return res.json()
})
.then(res => {
// 2. array for storing url's retrieved from response
var urlArray = []
if (res.length > 0) {
// 3. Push url inside urlArray
res.map(data => urlArray.push(data.url))
}
// 4. an array of urls
return urlArray
})
.then(urls => {
// Return an promise which will return "JSON response" array for all URLs.
// Promise.all means “Wait for these things” not “Do these things”.
return Promise.all(urls.map(url => {
// Take url fetch response, return JSON response
return fetch(url).then(res => res.json())
}
))
})
.then(res => {
// Store all objects into array for later use
var objArr = res; return objArr
})
//.then(...)
You have a little error in your code.
It's missing }) before .catch
With it you can use your data in the array.
componentDidMount(){
console.log('Fetch');
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
var productUrlArr = [];
for (var i = 0; i < json.length; i++) {
productUrlArr.push(json[i].url);
}
console.log(productUrlArr);
}).catch((err) => {
console.log('error', err);
});
}
Hope it helps.
It simple. First get all the url first like you did. Then map and pass it into Promise.all.
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
Promise.all(json.map(product =>
fetch(product.url).then(resp => resp.text())
)).then(texts => {
// catch all the data
})
}).catch((err) => {
console.log('error', err);
});

Ionic 2 - Passing ID from json to child (details) page

I have a provider service that calls get requests from my API. I then have a listing page whereby you can scroll though many recipes. What I am struggling with is taking the ID of each recipe and passing it to the details page as this needs to be included within.
My service request is for the listing is
loadCategory1() {
var url = "http://api.yummly.com/v1/api/recipes?_app_id=////&_app_key=////";
if (this.Category1) {
return Promise.resolve(this.Category1);
}
return new Promise(resolve => {
this.http.get(url + "&allowedAllergy[]=396^Dairy-Free&allowedAllergy[]=393^Gluten-Free&maxResult=50&start=10")
.map(res => res.json())
.subscribe(data => {
console.log(data);
this.Category1 = data.matches;
resolve(this.Category1);
});
});
}
and I currently have a separate one for my details as well
loadDetails() {
if (this.details) {
return Promise.resolve(this.details);
}
return new Promise(resolve => {
this.http.get('http://api.yummly.com/v1/api/recipe/French-Onion-Soup-The-Pioneer-Woman-Cooks-_-Ree-Drummond-41364?_app_id=//////&_app_key=//////')
.map(res => res.json())
.subscribe(data => {
console.log(data);
this.details = data;
resolve(this.details);
});
});
}
As you can see in the details request i have French-Onion-Soup-The-Pioneer-Woman-Cooks-_-Ree-Drummond-41364 This needs to be dynamic by taking the ID from each recipe. Example is below.
Within each .ts file I have the following
loadRecipes(){
this.apiAuthentication.loadCategory1()
.then(data => {
this.api = data;
});
}
This allows me to call the request.
I'm at the point now where I have no clue what to do so some help would be great.
Your DetailsService can be something like this:
loadDetails(detailsId: string) {
return new Promise(resolve => {
this.http.get('http://api.yummly.com/v1/api/recipe/'+detailsId+'?_app_id=//////&_app_key=//////')
.map(res => res.json())
.subscribe(data => {
console.log(data);
this.details = data;
resolve(this.details);
});
});
}
Navigate to DetailsPage with arguments:
this.navCtrl.push(DetailsPage,{
recipe: recipe
});
And you can call DetailsService inside DetailsPage by using code like this:
loadDetails(){
this.apiAuthentication.loadDetails(this.recipe.id)
.then(data => {
this.details = data;
});
}