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).
Related
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
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'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
}
I'm having a little trouble trying to retrieve Mongoose.count values from a function in my back end API within my mean.js application. Everything is routed correctly as far as I can see, and seems to be working absolutely fine until I get to the front end angular code and try to retrieve the data from the API via a service and $resource.
Here's my back end API function to retrieve the count of listings in a particular category, where the category is passed in correctly as parameter.
exports.getListingCountForSpecificCategory = function(req, res, next, category) {
Listing.count({
category: category,
quantityAvailable: { $gt: 0 },
listingActive: true
}, function(err, count){
if(err) {
console.log(err);
} else {
console.log(category + ': ' + count);
res.jsonp(count);
}
});
};
This runs correctly and within the console.log()
console.log(category + ': ' + count);
The category count is returned correctly. So, everything working correctly!
Until I get to retrieving the count value with angular on the front end. Here's the function I've written:
$scope.listingsCount = function(category) {
$scope.catCount = Listings.listingsCategoryCount.query({
category: category.alias
});
$scope.catCount.$promise.then(function(count){
category.listingCount = count;
});
};
When this function is run and the category object is passed into it, instead of it retrieving the count value e.g. 14, it seems to retrieve a resource promise object instead, with the count value nowhere to be seen. I've looked over the code a few times and can't for the life of me figure out why.
Here's the service I'm using, just in case you need to see it.
listingsCategoryCount: $resource('listingscount/:category', {
category: '#_category'
}, {
query: {
method: 'GET',
isArray: false
}
}),
It's a mystery to me why the count value isn't being returned. I may be going about this incorrectly of course. Any help would be greatly appreciated.
Thanks
https://docs.angularjs.org/api/ngResource/service/$resource:
On success, the promise is resolved with the same resource instance or collection object, updated with data from server.
Change this: res.jsonp(count); to res.jsonp({ count: count }); and it should work: you'll get a Resource object with property count.
I would like to get the list of users ordered by name and with the new users first.
I've used the documentation reference: http://quickblox.com/developers/Users#Sort
I've trying this code but it is not working at all:
function QBlistUsers(page) {
var userParams = {};
var page = currentPage;
{userParams.perPage = itemsPerPage;}
{userParams.pageNo = page;}
{userParams.order = ['desc','string','full_name'];}
//{userParams.order = 'desc+string+full_name';} // I've try this too, instead of the previous line
//load new rows per page
QB.users.listUsers(userParams, function(err, response){...}
The response is simply ignoring the param "order". I'm I doing something wrong?
thanks for helping
Look at new version of JS SDK 1.2.0:
http://quickblox.com/developers/Javascript
var params = {
order: { sort: 'desc', field: 'full_name' },
per_page: itemsPerPage,
page: page
};
QB.users.listUsers(params, function(error, response){
// callback function
});
Current version of WebSDK supports only 'in' parameter from Users filters. But we are already working on new version which will have all these filter cases. I think, through two / three days it will be released.
Try passing the order parameter like below and let me know whether it is working or not.
QB.users.listUsers({ order:'desc'+'string'+'full_name'}, function(error, response){
if(error) {
console.log(error);
} else {
// Success
}
});