I'm trying to write a fetch to get data from an api. The data within the network tab shows the correct data that I want to update to my html, but the data that ends up there is always the previously returned data currently.
For example, I load the page with quantity selected = 1, the price per item returns properly as 2.25, when I switch to 6, the expected price per item returned is 2.14 (discount taken care of in another script) this is the price shown in the network tab. Unfortunately this isn't the price that gets updated into my html span. it will still show the 2.25 until I make another selection, then it will update to the 2.14. This is the problem, my html always seems to be loading the previously calls result. Any help would be greatly appreciated. Thankyou!
<script>
var span = document.getElementById("price-span");
//Call to cart API
async function start() {
const headers = new Headers();
headers.append('pragma', 'no-cache');
headers.append('cache-control', 'no-cache');
await fetch('/cart.js', {
method: 'GET' , headers: headers
})
.then(promise => promise.json())
.then(data => render(data))
}
//Helper Functions
async function render(data) {
span.textContent = "$" + insertDecimal(data.items[0].discounted_price) + " CAD";
console.log(data.items[0].discounted_price)
}
function insertDecimal(num) {
return Number((num / 100).toFixed(2));
}
</script>
Note: The total price functions properly and was written previously by someone else
picture for ref
how the start is called
Related
Created a simple react-admin application that pulls from a custom rest api. First page is displayed (default 10 per page. Click the Next button and nothing happens (still sends page=1 to the api). Click a second time and the page advances to page 2 (page=2), as expected. Click the third time and goes back to page 1 (page=1).
Then, if you click a fourth time, it goes page 2, then click again, goes to page 3, then click again, goes back to page 1. It continues with this pattern, each round, getting one page further before going back to page.
I'm able to get the correct results when calling the custom API outside of the react-admin app. I created a custom dataProvider to communicate with the API and maybe there's a problem with the getList function, but I can definitely see the page number passed into this function and it lines up with the odd results (page 1, then 1, 2, 1, then 1, 2, 3, 1, etc. The custom API expects the following query string for pagination: ?limit=10&page=1&orderBy=id&orderDir=ASC
The original react-admin tutorial returns 10 records. When I set the page limit to 5, it does seem to work OK (advances to page 2 on the first click of Next), but without more records, it's hard to test it completely. But my guess is it would work, since it is most certainly a problem with my code or the API (although, as I said, the API works outside the react app).
Here's my getList function:
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const tokens = localStorage.getItem('tokens');
const objToken = JSON.parse(tokens);
options.user = {
authenticated: true,
token: `Bearer ${objToken.accessToken}`
};
return fetchUtils.fetchJson(url, options);
};
export default {
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const { q } = params.filter;
// Pagination and sort
let query = `limit=${perPage}&page=${page}&orderBy=${field}&orderDir=${order}`;
// Filter?
let useResource = '';
let useFilter = '';
if (q == null) {
// No filter: Use <resource>/ url
useResource = resource;
} else {
// Filter: Use append url with /find
useResource = `${resource}/find`;
useFilter = q;
console.log('useFilter: ', useFilter)
query += `&searchText=${useFilter}`;
}
const url = `${apiUrl}/${useResource}?${query}`;
return httpClient(url)
.then(({ json }) => ({
data: json.results,
total: json.totalRows,
}));
}, ...
Here's a screen shot of issue:
EDIT:
It looks like the correct query string is being sent but immediately after the first Next page click (page=2), page=1 is automatically sent again, returning to page one. This seems to be the case with subsequent Next clicks, as well. Thanks for helping out a newbie. But I just can't figure out why extra calls are being made returning to page 1.
Fixed in react-admin 3.4.3.
I updated using npm update and pagination works correctly.
I have exactly behavor with react 4.x.x
What i was expecting:
Going to next page when cliking on next, with react-admin 3.19 this is how my application worked
What happened instead:
when you click on the next page, pagination resets to 1 !
also, it does not take into account the pagination that I define.
on chrome default perPage is 5, even when i set it 10.
chrome_pagination_issue
on firefox default perPage=10, but i have the same issue
firefox_pagination_issue
Other information:
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
console.log(params);
const query = {
...fetchUtils.flattenObject(params.filter),
_sort: field,
_order: order,
_start: (page - 1) * perPage,
_end: page * perPage,
_resource:resource
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => {
if (!json.hasOwnProperty('totalElements')) {
throw new Error(
"The numberOfElements property must be must be present in the Json response"
);
}
return {
data: json.content,
total: parseInt(json.totalElements,10)
};
});
}
#4658
Environment
React-admin version: 4.0.1 , 4.0.2
React version:18
Strict mode disabled
Browser: chrome, firefox
My backend is spring boot rest api
I have a total of 250 items and I'm displaying 5 of them i.e. pageSize is set to 5.
and I've set the paginator.length property to items.length
Now, there are two problems here:
Even though I've manually set the paginator.length but when I console.log the properties of paginator, it shows 5 (Which is the number of items I'm displaying per page). hence the next button of paginator is disabled.
How do I enable the next button, so that when it is clicked I would know and request the next data to server.
Remember: I'm using only back-end pagination for this regard.
Pagination information is passed in response headers from the server.
Here is my code
pageIndex=1;
pageNumber;
pagination;
ngOnInit(): void {
this.paginator.pageSize = 5;
this.paginator.pageIndex = this.pageIndex;
this.salesReportService.getDailyReport().subscribe((response) => {
console.log('Response of sales report : ', response);
this.reports = response.body;
this.pagination=response.headers.get('X-Pagination');
console.log('PaginationInformation: ',this.pagination)
this.paginator.length=this.pagination.TotalCount;
console.log('paginator properties: ', this.paginator);
console.log('paginator hasNextpage(): ', this.paginator.hasNextPage());
this.dataSource = new MatTableDataSource(this.reports);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}, (error) => {
console.log('Error occured while fetching sales report: ', error);
});
}
// here I'd call the function to get the next data when the next button of paginator is clicked.
// something like this
this.salesReportService.getReports(pageNumber:2)
This is because you set paginator length in the first place and later trying to execute below lines
this.dataSource = new MatTableDataSource(this.reports);
this.dataSource.paginator = this.paginator;
which makes paginator to set length to data available in datasource.
So, after the above two lines keep the below line in your code.
this.paginator.length=this.pagination.TotalCount; // this.paginator.length= 'No Of Records';
Hope it helps!
This question already has answers here:
XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header
(11 answers)
Closed 3 years ago.
I am trying to get data from AWS JSON. It is not on my domain and I can't get the data. The error that chrome gives is below Could anyone review my code and give me some tips?
Second, I was using Insomnia just using the link and was able to get the specific data point that I wanted using $.0.Ed0320. How do I translate this into JavaScript.
I have tried using a xmlHttpRequest.
Here is the JSON:
[
{
"Ed0320": "8.010886",
"TmStamp": "2019-08-07 15:15:00",
"Ed0340": "21.15973",
"Ed0305": "0.2966875",
"Ed0313": "3.344086"
},
{
"Ed0320": "6.761719",
"TmStamp": "2019-08-07 15:10:00",
"Ed0340": "17.47292",
"Ed0305": "0.2349026",
"Ed0313": "2.789934"
}
]
Here is my XML:
function reqListener() {
// parse the the data
var data = JSON.parse(this.responseText)
// create an array from the required data (result.Stats -> watch the capital 'S')
var mappedData = data[0];
// display data
document.getElementById('demo').innerHTML = mappedData.join()
}
function loadData(url) {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", url);
oReq.send();
}
Chrome gives the error of Access to XMLHttpRequest at <fileName> from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
function calcUVI(Ed0305, Ed0313, Ed0320, Ed0340){
total305=(Ed0305*0.8058)
total313=(Ed0313*0.0887)
total320=(Ed0320*0.0324)
total340=(Ed0340*0.0131)
UVI = total305 + total313 + total320 + total340
return UVI
}
Also, I would like to change the url based on this function below. Is there a way to input this into the fetch?
function changeText(){
var d = new Date();
var year = d.getFullYear();
var month = d.getMonth() + 1;
var day = d.getDate();
if (month <= 9){
month = '0'+month;
}
if (day <= 9){
day = '0'+day;
}
var dateTotal = year + month + day;
url = "https://cors-escape.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=" + dateTotal;
console.log(url);
}
Thank you in advance for your help. I am really new to all of this.
1. Fetching the data
I'm not quite sure, what you want to achieve, but here's a snippet, that
Makes a request to cors-anywhere.herokuapp.com to "proxy" the query of the "target" URL
Returns a Promise from the queried URL, so we can wait for it to resolve in other parts of the code
Filter and map the resulting data - I didn't understand what data would you like to get from the resulting array and how did you want to handle that data
// self calling function to wrap the await in
// an async function (so the await is not "top level")
(async function() {
// waiting for the returned Promise to resolve
// if you console.log() the data constant, then you'll
// see that it's the full dataset (in JSON format) that you
// got from simply calling the AWS URL
const data = await fetchURL()
// filtering the array for objects that have a key of "Ed0320"
const filtered = data.filter(e => typeof e['Ed0320'] !== "undefined")
// mapping the values of all "Ed0320" from the array
const mapped = data.map(e => e['Ed0320'])
// simple output, so you can check the values
console.log(filtered);
console.log(mapped)
})();
// fetch request using cors-anywhere
function fetchURL() {
// creating the URL to be called:
// first part is cors-anywhere, second part is
// the real URL to be queried
const url = 'https://cors-anywhere.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=20190807'
// returning a Promise, as fetch() is an asynchron
// function, so it's not resolved immediately
return new Promise((resolve, reject) => {
// using fetch() instead of the XMLHttpRequest()
// fetch returns a Promise, so you can use .then() and
// .catch() chained after it
fetch(url)
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
So, your code had CORS issues (CORS: Cross-Origin Resource Sharing), that had to be handled before you could get the data. I used the cors-anywhere on Heroku to overcome this obstacle. You can set your cors-anywhere service by installing the module from npm (here: https://www.npmjs.com/package/cors-anywhere).
2. EDIT: Processing the data
The answer is edited as the question was expanded with more information.
This new snippet does calculate the UVI based on the results from the API (the URL is generated dynamically based on current date).
Please note:
you can make all your functions shorter (like don't create variables for values that you only need for one calculation, where you only return the result - e.g. current date string or the UVI calculation)
the "creation" of the date string is modified: I used string interpolation and ('0' + d.getDate()).slice(-2) to force two digits long months and days
when you create (design) a function try to keep it simple - it should do only one thing (so don't create a function that generates current date string and attaches it to the query string. Maintenance will be much easier this way.
the calcUVI() function receives its parameters via destructuring the array element it's called with. Get familiar with destructuring - it's not that hard, and makes your code easier to read and understand. (You can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
(async function() {
// modified fetchURL() - the URL parameter is passed to
// the API query function
const data = await fetchURL(urlToQuery())
// calling calcUVI() function with the first data
// entry of the data array
const UVI = calcUVI(data[0])
// displaying result in the console
console.log("UVI:", UVI)
})();
function fetchURL(url) {
return new Promise((resolve, reject) => {
// the url parameter is received when the function
// is called - it's not a constant anymore
fetch(url)
.then(r => {
resolve(r.json())
})
.catch(err => {
reject(err)
})
})
}
// creating and returning the API URL dynamically -
// date string is added
function urlToQuery() {
// the query URL base is here - and the date string
// at the end is created dynamically
return `https://cors-anywhere.herokuapp.com/https://tepfsail50.execute-api.us-west-2.amazonaws.com/v1/report/metguv?rptdate=${getDateString()}`
}
// creating and returning the date string
function getDateString() {
const d = new Date();
return `${d.getFullYear()}${('0' + (d.getMonth() + 1)).slice(-2)}${('0' + d.getDate()).slice(-2)}`
}
// calcUVI receives the parameters destructured from
// the first element of the array (it's called in
// the main async function)
function calcUVI({
Ed0305,
Ed0313,
Ed0320,
Ed0340
}) {
// no need to store these elements as variables, as the single
// Ed... values are not used anymore in the process (a
// total is returned, so just add them up)
return Ed0305 * 0.8058 + Ed0313 * 0.0887 + Ed0320 * 0.0324 + Ed0340 * 0.0131
}
I have a jqGrid that points at an external API, which I have no control over. This external API has two endpoints:
Data Endpoint - Returns the table row data
Count Endpoint - Returns pagination counts etc.,
Based on user input the jqGrid filter gets converted into the appropriate query-string to filter the external API's Data and Count Endpoints.
I have jqGrids url being dynamically built based off of user input and targets the Data Endpoint...and during the loadBeforeSend() event it calls the Count Endpoint to get the latest pagination information based on users filter.
I am using the jsonreader capabilities:
jsonReader: {
root: 'products',
id: 'id',
records: function () {
return gridTotal;
},
total: function () {
// var totalPages = (gridTotal + reqOptions.limit-1) / reqOptions.limit;
var totalPages = Math.ceil(gridTotal / reqOptions.limit);
console.log('totalPages: ' + totalPages);
return totalPages;
},
page: function () {
//var totalPages = Math.ceil(gridTotal/20);
console.log('currentPage: ' + reqOptions.page);
return reqOptions.page;
}
},
Sample of the loadBeforeSend method:
loadBeforeSend: function (xhr, settings) {
settings.url = _newUrl || endpointURL;
// Lets fetch our data count...this may change as items get published so lets fetch during load
products.count(accessToken, _filterQuery)
.success(function (resp) {
// This is the total number of products that match our current search
gridTotal = resp.count;
}).catch(function (err) {
console.error(err);
});
}
Fetching from the Data Endpoint works really well, the issue is how to call the Count Endpoint and update the pagination data.
Tried the following:
Using setGridParam for records, last_page, etc.,
Using getGridParam('reccount')
Just update the html to look correct (not effective since paging will be off)
Is there a way to
Manually fire off the XHR for jqgrid URL...so I can request the Count first and when it returns then go fetch the Data?
Rerun the jsonreader functionality once the Count returns and gridTotal is set
Use a promise like structure to resolve records count
Updated to show #Oleg solution
loadBeforeSend: function (xhr, settings) {
settings.url = _newUrl || endpointURL;
// Lets fetch our data count...this may change as items get published so lets fetch during load
products.count(accessToken, _filterQuery)
.success(function (resp) {
// This is the total number of products that match our current search
gridTotal = resp.count;
gridTotal = resp.count;
grid.jqGrid('setGridParam', {
page: gridOpts.jsonReader.page(),
records: gridTotal,
lastpage: gridOpts.jsonReader.total()
});
grid[0].updatepager(false, true);
}).catch(function (err) {
console.error(err);
});
}
I hope that I correctly understand your problem. In the case you can first make loading of the main data from the Data Endpoint. Then (inside of loadComplete) you can start new $.ajax request manually to get the data from the Count Endpoint and to update the pagination data inside of success callback of the $.ajax.
What you need to do for the updating the pager is:
setting of page, records and lastpage parameters of jqGrid based on the data returned from the Count Endpoint.
call of $("#grid")[0].updatepager(false, true); which will uses the above options and to refresh the information on the pager.
You can see in the old answer and example of usage of .updatepager(false, true).
I'm using this Gumroad-API npm package in order to fetch data from an external service (Gumroad). Unfortunately, it seems to use a .then() construct which can get a little unwieldy as you will find out below:
This is my meteor method:
Meteor.methods({
fetchGumroadData: () => {
const Gumroad = Meteor.npmRequire('gumroad-api');
let gumroad = new Gumroad({ token: Meteor.settings.gumroadAccessKey });
let before = "2099-12-04";
let after = "2014-12-04";
let page = 1;
let sales = [];
// Recursively defined to continue fetching the next page if it exists
let doThisAfterResponse = (response) => {
sales.push(response.sales);
if (response.next_page_url) {
page = page + 1;
gumroad.listSales(after, before, page).then(doThisAfterResponse);
} else {
let finalArray = R.unnest(sales);
console.log('result array length: ' + finalArray.length);
Meteor.call('insertSales', finalArray);
console.log('FINISHED');
}
}
gumroad.listSales(after, before, page).then(doThisAfterResponse); // run
}
});
Since the NPM package exposes the Gumorad API using something like this:
gumroad.listSales(after, before, page).then(callback)
I decided to do it recursively in order to grab all pages of data.
Let me try to re-cap what is happening here:
The journey starts on the last line of the code shown above.
The initial page is fetched, and doThisAfterResponse() is run for the first time.
We first dump the returned data into our sales array, and then we check if the response has given us a link to the next page (as an indication as to whether or not we're on the final page).
If so, we increment our page count and we make the API call again with the same function to handle the response again.
If not, this means we're at our final page. Now it's time to format the data using R.unnest and finally insert the finalArray of data into our database.
But a funny thing happens here. The entire execution halts at the Meteor.call() and I don't even get an error output to the server logs.
I even tried switching out the Meteor.call() for a simple: Sales.insert({text: 'testing'}) but the exact same behaviour is observed.
What I really need to do is to fetch the information and then store it into the database on the server. How can I make that happen?
EDIT: Please also see this other (much more simplified) SO question I made:
Calling a Meteor Method inside a Promise Callback [Halting w/o Error]
I ended up ditching the NPM package and writing my own API call. I could never figure out how to make my call inside the .then(). Here's the code:
fetchGumroadData: () => {
let sales = [];
const fetchData = (page = 1) => {
let options = {
data: {
access_token: Meteor.settings.gumroadAccessKey,
before: '2099-12-04',
after: '2014-12-04',
page: page,
}
};
HTTP.call('GET', 'https://api.gumroad.com/v2/sales', options, (err,res) => {
if (err) { // API call failed
console.log(err);
throw err;
} else { // API call successful
sales.push(...res.data.sales);
res.data.next_page_url ? fetchData(page + 1) : Meteor.call('addSalesFromAPI', sales);
}
});
};
fetchData(); // run the function to fetch data recursively
}