I have a JQuery datatable that loads data via an ajax request. There is a form by the table that lets the user filter the results on the server. When the user changes the filter form, I call fnReloadAjax() with the new filter data, and the table redraws with the new, filtered results.
The problem I have is that the pagination sticks, even if the table no longer has enough items to reach the current page. So if the table originally had 20 items, and the user was on page 2 (10 items per page), and they change the filter so only 5 items are returned, the table displays no items and shows this at the bottom:
Showing 11 to 5 of 5 entries
It's still on page 2 even though there is only enough data for one page.
I have found numerous posts about trying to preserve the current page/row, but none showing a simple way to reset pagination to the first page. What is the simplest way to do this?
Here's a simplified version of my code for clarity:
$("#mytable").dataTable({
bStateSave: false,
fnServerData: function (sSource, aoData, fnCallback) {
return $.ajax({
type: "POST",
url: url,
data: {filter: filterValue}
});
}
});
$("#myForm").submit(function(e) {
table.fnReloadAjax();
return false;
});
You could explicitly jump to the first page after reloading, see http://datatables.net/api#fnPageChange
$("#myForm").submit(function(e) {
table.fnPageChange(0);
table.fnReloadAjax();
return false;
});
Accepting the solution given by #Gigo:
$("#myForm").submit(function(e) {
table.fnPageChange(0);
table.fnReloadAjax();
return false;
});
This have a problem, it sends two request to the server.
i have found that the fnPageChange does it at the first time.
$("#myForm").submit(function(e) {
table.fnPageChange(0);
return false;
});
This can be solved by implementing the functions to save and load the state of the datatable and resetting the start point - example below
"fnStateSave": function (oSettings, oData) {
localStorage.setItem( 'MyDataTable', JSON.stringify(oData) );
},
"fnStateLoad": function (oSettings) {
var settings = JSON.parse( localStorage.getItem('MyDataTable') );
settings.iStart = 0; // resets to first page of results
return settings
},
As fnStateLoad is called when the table is reloaded - e.g. a new filter is applied - the paging is reset to the start.
fnStateSave is called each time you retrieve the next page of results
This approach avoids the overhead of an additional request back to the server
Related
currently I am using datatables with multiple selection. When I select 3 rows based on picture here (from top to bottom), my console.log returns this value (return result every time row is being selected). The value that I need to post to next page is this, since it's the latest result:
["SN20171110", "SN20171111", "SN20171113"]
and so I did console.log again to check the value in ajax post without getting to next page but the result was random (the order):
https://i.stack.imgur.com/zVkrW.png
https://i.stack.imgur.com/Irf1k.png
https://i.stack.imgur.com/9Nn1n.png
So what I get in the next page was the bottom(latest) result from console.log mentioned above.
Is there any way to pass only the bottom result or javascript variable?
Here is my Datatable selection code:
var newdata = $.map(example.rows('.selected').data(), function (item) {
$(this).toggleClass('selected');
return item[1];
});
var test = newdata;
console.log(test);
$('#nextButton').click(function(e){
$.ajax({
url : 'enrollmentDevice.php',
method : 'POST',
data : { test },
beforeSend: function(){
console.clear();
},
success : function(new2) {
console.log(test);
}
Thanks in advance.
Ok I solved the problem by posting the value to hidden form and POST to next page using
<form action="nextpage.php" method="POST"></form>
instead of using Ajax.
But still not sure what cause the problem. Any input would be appreciate. Thank you.
I have an app in which I have multiple search sources. Previously, the users had to choose in what source to search in before searching. If they did not choose, the app would default to one of the options.
However, now they want to search in all the sources at the same time. This is fine enough, but the problem is that when one of the searches returns, it overwrites the previous search result. Pretty much expected behavior. What I basically want is to append the new results to the already open autocomplete menu, instead of overwriting the old results. Naturally, the autocomplete menu would have to empty when it closes.
I guess that this is possible to do, but what approach is the best? I could just have an array I guess, which I append results to and then overwrite _renderMenu to use this array instead of the items one that is passed to the function. Then empty said array at the close event.
Is this the best way to go though? Or is there a more elegant solution?
Some code:
Ok, so searchAction is called by jquery autocomplete eventually. In collection.search I do the ajax call, here the URL is created based in the this parameter, then respondWhithData is called and maps the search result to a proper format (ie value and label for the autocomplete menu). After reponse is called from respondWithData, jquery automagically renders the resultsmenu. Thus, I probably have to overwrite the reponse event function as well as the _renderMenu and possibly _renderItem, yes?
searchAction: function(searchTerm, collection, response){
var self = this;
$.when(collection.search(searchTerm, this)).then(function(data) {
self.respondWithData(data, response);
});
},
respondWithData : function(data, response) {
if (data.length > 0) {
var responseVal = _.map(data, this.mapData);
this.checkResponseCount(responseVal);
response(responseVal);
}
else {
response(this.emptyResult());
}
},
To be clear, the problem is not the multiple search itself, but rendering the asynchronos results. I want to render the first results that come back, and then appends the rest as soon as they are returned from the server.
Edit 2:
Just tried to edit ui.content in the autocompleteresponse event, but any edit does not take once it renders for some reason...
Edit 3: Ah, ui.content can only be modified directly, not changed. If I push every single change instead of concating two arrays ui.content shows what I want.
It works I guess, but its not perfect.
I can figure how looks your scenario but I'm guessing:
You should have like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").html(data)
}
});
}
etc
Instead of overwritting the #myResultsDiv you need to Append the results like:
function search1() {
$.ajax({ ...
success: function(data) {
$('#myResultsDiv").append(data)
}
});
}
Edit: You can also do something like this:
var resultsArray = [];
var searchDone = 0;
var totalSearchs = 5; //assuming 5 searches
function search1() {
function search1() {
$.ajax({ ...
success: function(data) {
//APPEND data to resultsArray
searchDone++;
if(searchDone==totalSearch) //syncronize the 5 searchs before render
renderSearchs(resultsArray);
}
});
}
I have a search filter on my site whereby when a user unchecks a box, an ajax call is posted to the server, returning new, narrowed down search results.
now, however, the user has the ability to uncheck about 20 boxes at the same time, which forces me to make 20 different ajax calls -- this is very slow
any ideas on how to pass the 20 different ajax calls into one so as to speed up things?
here is my js:
// to allow us to process the "only" buttons, which don't register the click event we're looking for
$("input[type=checkbox]").change(function() {
// remove the original, unfiltered results
$('#original_results').css("display", "none");
// which filter section does the filter belong to
var filter_section = $(this).attr('name');
// should it be filtered out or left in
var remove = $(this).prop('checked');
// if needs to be filtered
if (!remove)
{
// add it to our filter list for the specified section
filters[filter_section].push(this.value);
}
else
{
// take it off the list for the specified section
var index = filters[filter_section].indexOf(this.value);
if (index > -1)
{
filters[filter_section].splice(index, 1);
}
}
doAjax();
});
// ajax function
function doAjax() {
// get slider values
var ranges = $('#pay_range').slider('values');
// define data to post
var data = {
min: ranges[0],
max: ranges[1],
filters: filters,
criteria: criteria
};
// post data
$.ajax({
type: 'POST',
url: '/results/search_filter',
data: data,
beforeSend: function() {
$('#search_results').html('Updating your query');
},
success: function(response) {
$('#search_results').html(response);
},
dataType: "html"
});
}
As I understand it, you want your AJAX call to only happen once per action, even if the action includes changing multiple checkboxes.
I've achieved this using javascript's setTimeout() to "buffer" events. When a checkbox is changed, a short timeout is set which will fire the AJAX. If another checkbox is changed within that time period, the timeout will be re-set (instead of firing the AJAX twice). The AJAX only fires once at the end of the timeout.
// initialize timer variable
var ajaxtimer;
// function to run AJAX
function runajax() {
alert('ajax');
}
// clicking a checkbox sets all checkboxes to the same state
$("input[type=checkbox]").on('click', function () {
$('input[type=checkbox]').prop('checked', this.checked).change();
});
// fires on checkbox change, sets short timeout
$("input[type=checkbox]").on('change', function () {
clearTimeout(ajaxtimer);
ajaxtimer = setTimeout(runajax, 50);
});
WORKING EXAMPLE (jsfiddle)
Edit:
I saw the link you posted and made the following adjustments:
I defined this variable at the top of the file:
var ajaxtimer;
On line 156 of results.js, I changed:
doAjax();
to
clearTimeout(ajaxtimer);
ajaxtimer=setTimeout(doAjax,50);
Here's a jsfiddle. The layout is butchered, but you can see that clicking an "only" link results in only one ajax call, rather than one call for every checkbox that was changed.
Assuming I have about 500 rows of data and are within a grid showing 40 rows per page.
I been thinking about how the grid works is that the 500 rows to display the next page with the data correspond.?
I have 2 options ....
1 The first is that you make a single queryto BD, and keep 500 rows in memory and work from there and cut each time you pass page.
2nd Second, run the query to the database, Cut to show the necessary rows and then display. And every time I pass page make a new query, bringing the 500 and go cutting data and showing only what is needed.
So the question is what is the ideal operation that should have a grid to handle the data? Making the best use in performance.
If you try to take a look at more general case, which is there are N rows in a dataset and you need to show M on a page, it would be clear that with N big enough, in most cases, it does not make sense to load all data in memory ( however you might do this in order to warm up a cache etc )
So usually you are loading one page at a time from database, passing pageNumber and pageSize parameters to the query.
Also you might use client side caching and save old page when navigating to new one. Having LocalStorage, WebSql etc in a browser makes it really easy.
function getNewPage(pageNumber, pageSize) {
var cacheKey = pageNumber + '_' + pageSize
if (myCache.contain(cacheKey)) {
return $.deferred().resolve(myCache.get(cacheKey))
}
return $.ajax( url , { pageSize:pageSize, pageNumber: pageNumber }, function (data) {
myCache.set(cacheKey, data)
return data
} )
}
var myCache = {
contain: function(key) {
return !!sessionStorage.getItem(key)
},
get: function(key) {
return JSON.parse(sessionStorage.getItem(key)
},
set: function(key, o) {
sessionStorage.setItem(key, JSON.stringify(o))
}
}
and somewhere in some button click handler:
getNewPage(0,10).done(function(data) {
var template = _.template('<tr><td>{field1}</td><td>{field2}</td></tr>')
$myTableTbody.html(data.map(template))
})
this question has already been answered multiple times. but here I want to site that if the code provided in the documentation can't be achieved without additional codes, why it has been given there in first place. Its simply misleading. The code given in the documentation achieves paging, but while sorting, the grid data simply disappears.
Correct me if I am wrong.
jQuery("#gridid").jqGrid({
...
datatype: 'json', // can be xml
loadComplete : function () {
jQuery("#gridid").jqGrid('setGridParam',{datatype:'local'});
},
onPaging : function(which_button) {
jQuery("#gridid").jqGrid('setGridParam',{datatype:'json'});
},
...
});
You don't posted the exact reference to the documentation where you get the code. I found it here.
jqGrid is open source product which you get for free. It's practical, but you should understand, that in the case the product and its documentation could not be perfect. The part of code which you referenced could work probably in some very old version of jqGrid, but it's wrong code in the current version of jqGrid. The sense of implementing "Client side sorting, but server side paging" is very suspected at all. My old answer about the subject you would find here. I would rewrite some part of the answer now, but in general the code tested with old versions could be not full compatible with the new version of jqGrid.
I can say there is no place where intentional misleading has happened. They are giving the pluging for free though it is a very huge plugin. And the work done by people like Oleg is making it more perfect. For you question, the code related to "client side sorting and server side paging" here is the code that can solve your problems. And this was taken with the help of some old answer given by Oleg.
This is my version of code,
loadComplete: function(data) {
var $this = $(this);
if ($this.jqGrid('getGridParam', 'datatype') === 'json') {
// because one use repeatitems: false option and uses no
// jsonmap in the colModel the setting of data parameter
// is very easy. We can set data parameter to data.rows:
$this.jqGrid('setGridParam', {
datatype: 'local',
data: data.userdata,
pageServer: data.page,
recordsServer: data.records,
lastpageServer: data.total
});
// because we changed the value of the data parameter
// we need update internal _index parameter:
this.refreshIndex();
if ($this.jqGrid('getGridParam', 'sortname') !== '') {
// we need reload grid only if we use sortname parameter,
// but the server return unsorted data
$this.triggerHandler('reloadGrid');
}
} else {
$this.jqGrid('setGridParam', {
page: $this.jqGrid('getGridParam', 'pageServer'),
records: $this.jqGrid('getGridParam', 'recordsServer'),
lastpage: $this.jqGrid('getGridParam', 'lastpageServer')
});
this.updatepager(false, true);}
}
onPaging:function(){
/*this code is to fix the issue when we click on next page with some data in filter tool bar
* along with this in grid definition( in filterToolbar ) we have to return true in "beforeClear "event
* */
var data = $(this).jqGrid("getGridParam", "postData");
data._search = false;
data.filters=null;
data.page=$(this).jqGrid("getGridParam","page");
/* comment this line if you disable filter toolbar*/
$(this)[0].clearToolbar();
//Here making _search alone false will not solve problem, we have to make search also false. like in below.
$(this).jqGrid('setGridParam', { search: false, postData:data });
var data = $(this).jqGrid("getGridParam", "postData");
/*this is to fix the issue when we go to last page(say there are only 3 records and page size is 5) and click
* on sorting the grid fetches previously loaded data (may be from its buffer) and displays 5 records
* where in i am expecting only 3 records to be sorted out.along with this there should be a modification in source code.
*/
$(this).jqGrid("clearGridData");
/* this is to make the grid to fetch data from server on page click*/
$(this).setGridParam({datatype: 'json'}).triggerHandler("reloadGrid");
}
For the modification that you have to do in source code is , see this answer..