Can jqGrid summary footer be populated without userdata server request? - jqgrid

I have poured through every question asked on this website related to populating the jqGrid summary footer WITHOUT the userdata being a server request. And the jqGrid wiki resource and just cannot find an answer. Is this possible to do?
I use the jqGrid in many typical ways as part of my admin portal but have one particular use where I want the look & feel of the jqGrid to start out as an empty UI container of sorts for some user interaction adding rows through a form which doesn't post on Submit (thanks to Oleg's great script below) but simply adds the row and updates the summary footer with the values from the newly added row. I have 'summarytype:"sum"' in the colModel, "grouping:true", footerrow: true, userDataOnFooter: true, altRows: true, in the grid options but apart from the initial values supplied by the userdata in the local data string, the summary footer values never change. It seems like no one wants to touch this subject. Is it because the primary nature of the jqGrid is it's database driven functionality? I use about 15 instances of the jqGrid in it's database driven stance (many of which are in a production service now) but need to use it for consistency sake (all my UI's are inside jqTabs) initially as a client side UI with no server request (everything will be saved later to db) but I am pulling my hair out trying to manipulate the summary footer programmatically on and by the client only. Can anyone suggest how to cause values to the summary footer to update when a new row is added the values of which would be summed up with any existing rows and which does not involve any posting to server, just updates inside the grid?
The code supplied is kind of long and based primarily on user Oleg's solution for adding a row from a modal form without posting to the server. I've changed the local array data to JSON string simply to better understand the notation as I'm used to xml. The jsonstring initializes the grid with one default row for the user to edit. I've left out the jsonReader because the grid would not render with it.
In a nutshell then what I want to do is to have the summary footer update when a new row is added to the grid (no posting to server occurring at this point) or edited or deleted. When a certain set of values is achieved the user is prompted by a displayed button to save row data to db.
var lastSel, mydata = { "total": 1, "page": 1, "records": 1, "rows": [{ "id": acmid, "cell": ["0.00", "0.00", "0.00", "0.00"]}], "userdata":{ "mf": 0.00, "af":0.00,"pf":0.00,"cf":0.00 }}
grid = $("#ta_form_d"),
onclickSubmitLocal = function (options, postdata) {
var grid_p = grid[0].p,
idname = grid_p.prmNames.id,
grid_id = grid[0].id,
id_in_postdata = grid_id + "_id",
rowid = postdata[id_in_postdata],
addMode = rowid === "_empty",
oldValueOfSortColumn;
// postdata has row id property with another name. we fix it:
if (addMode) {
// generate new id
var new_id = grid_p.records + 1;
while ($("#" + new_id).length !== 0) {
new_id++;
}
postdata[idname] = String(new_id);
//alert(postdata[idname]);
} else if (typeof (postdata[idname]) === "undefined") {
// set id property only if the property not exist
postdata[idname] = rowid;
}
delete postdata[id_in_postdata];
// prepare postdata for tree grid
if (grid_p.treeGrid === true) {
if (addMode) {
var tr_par_id = grid_p.treeGridModel === 'adjacency' ? grid_p.treeReader.parent_id_field : 'parent_id';
postdata[tr_par_id] = grid_p.selrow;
}
$.each(grid_p.treeReader, function (i) {
if (postdata.hasOwnProperty(this)) {
delete postdata[this];
}
});
}
// decode data if there encoded with autoencode
if (grid_p.autoencode) {
$.each(postdata, function (n, v) {
postdata[n] = $.jgrid.htmlDecode(v); // TODO: some columns could be skipped
});
}
// save old value from the sorted column
oldValueOfSortColumn = grid_p.sortname === "" ? undefined : grid.jqGrid('getCell', rowid, grid_p.sortname);
//alert(oldValueOfSortColumn);
// save the data in the grid
if (grid_p.treeGrid === true) {
if (addMode) {
grid.jqGrid("addChildNode", rowid, grid_p.selrow, postdata);
} else {
grid.jqGrid("setTreeRow", rowid, postdata);
}
} else {
if (addMode) {
grid.jqGrid("addRowData", rowid, postdata, options.addedrow);
} else {
grid.jqGrid("setRowData", rowid, postdata);
}
}
if ((addMode && options.closeAfterAdd) || (!addMode && options.closeAfterEdit)) {
// close the edit/add dialog
$.jgrid.hideModal("#editmod" + grid_id,
{ gb: "#gbox_" + grid_id, jqm: options.jqModal, onClose: options.onClose });
}
if (postdata[grid_p.sortname] !== oldValueOfSortColumn) {
// if the data are changed in the column by which are currently sorted
// we need resort the grid
setTimeout(function () {
grid.trigger("reloadGrid", [{ current: true}]);
}, 100);
}
// !!! the most important step: skip ajax request to the server
this.processing = true;
return {};
},
editSettings = {
//recreateForm:true,
jqModal: false,
reloadAfterSubmit: false,
closeOnEscape: true,
savekey: [true, 13],
closeAfterEdit: true,
onclickSubmit: onclickSubmitLocal
},
addSettings = {
//recreateForm:true,
jqModal: false,
reloadAfterSubmit: false,
savekey: [true, 13],
closeOnEscape: true,
closeAfterAdd: true,
onclickSubmit: onclickSubmitLocal
},
delSettings = {
// because I use "local" data I don't want to send the changes to the server
// so I use "processing:true" setting and delete the row manually in onclickSubmit
onclickSubmit: function (options, rowid) {
var grid_id = grid[0].id,
grid_p = grid[0].p,
newPage = grid[0].p.page;
// delete the row
grid.delRowData(rowid);
$.jgrid.hideModal("#delmod" + grid_id,
{ gb: "#gbox_" + grid_id, jqm: options.jqModal, onClose: options.onClose });
if (grid_p.lastpage > 1) {// on the multipage grid reload the grid
if (grid_p.reccount === 0 && newPage === grid_p.lastpage) {
// if after deliting there are no rows on the current page
// which is the last page of the grid
newPage--; // go to the previous page
}
// reload grid to make the row from the next page visable.
grid.trigger("reloadGrid", [{ page: newPage}]);
}
return true;
},
processing: true
}; //,
/*initDateEdit = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: 'dd-M-yy',
autoSize: true,
showOn: 'button', // it dosn't work in searching dialog
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
//$(elem).focus();
}, 100);
},
initDateSearch = function (elem) {
setTimeout(function () {
$(elem).datepicker({
dateFormat: 'dd-M-yy',
autoSize: true,
//showOn: 'button', // it dosn't work in searching dialog
changeYear: true,
changeMonth: true,
showButtonPanel: true,
showWeek: true
});
//$(elem).focus();
}, 100);
};*/
//jQuery("#ta_form_d").jqGrid({
grid.jqGrid({
// url:'/admin/tg_cma/ta_allocations.asp?acmid=' + acmid + '&mid=' + merchantid + '&approval_code=' + approval_code,
//datatype: "local",
//data: mydata,
datatype: 'jsonstring',
datastr: mydata,
colNames: ['ID', 'Monthly Fees', 'ATM Fees', 'POS Fees', 'Card to Card Fees'],
colModel: [
{ name: 'id', index: 'id', width: 90, align: "center", editable: true, editoptions: { size: 25 }, formoptions: { rowpos: 1, colpos: 1, label: "EzyAccount ID", elmprefix: "(*) " }, editrules: { required: true} },
{ name: 'mf', index: 'mf', width: 130, align: "right", formatter: 'number', editable: true, summaryType: 'sum', editoptions: { size: 25 }, formoptions: { rowpos: 2, colpos: 1, label: "Monthly Fee", elmprefix: "(*) " }, editrules: { required: true} },
{ name: 'af', index: 'af', width: 130, align: "right", formatter: 'number', editable: true, summaryType: 'sum', editoptions: { size: 25 }, formoptions: { rowpos: 3, colpos: 1, label: "ATM Fee", elmprefix: "(*) " }, editrules: { required: true} },
{ name: 'pf', index: 'pf', width: 130, align: "right", formatter: 'number', editable: true, summaryType: 'sum', editoptions: { size: 25 }, formoptions: { rowpos: 4, colpos: 1, label: "POS Fee", elmprefix: "(*) " }, editrules: { required: true} },
{ name: 'cf', index: 'cf', width: 130, align: "right", formatter: 'number', editable: true, summaryType: 'sum', editoptions: { size: 25 }, formoptions: { rowpos: 5, colpos: 1, label: "Card to Card Fee", elmprefix: "(*) " }, editrules: { required: true} }
],
rowNum: 5,
rowList: [5, 10, 20],
pager: '#pta_form_d',
toolbar: [true, "top"],
width: 500,
height: 100,
editurl: 'clientArray',
sortname: 'id',
viewrecords: true,
sortorder: "asc",
multiselect: false,
cellEdit: false,
caption: "Allocations",
grouping: true,
/*groupingView: {
groupField: ['id', 'mf', 'af', 'pf', 'cf'],
groupColumnShow: [true],
groupText: ['<b>{0}</b>'],
groupCollapse: false,
groupOrder: ['asc'],
groupSummary: [true],
groupDataSorted: true
},*/
footerrow: true,
userDataOnFooter: true,
altRows: true,
ondblClickRow: function (rowid, ri, ci) {
var p = grid[0].p;
if (p.selrow !== rowid) {
// prevent the row from be unselected on double-click
// the implementation is for "multiselect:false" which we use,
// but one can easy modify the code for "multiselect:true"
grid.jqGrid('setSelection', rowid);
}
grid.jqGrid('editGridRow', rowid, editSettings);
},
onSelectRow: function (id) {
if (id && id !== lastSel) {
// cancel editing of the previous selected row if it was in editing state.
// jqGrid hold intern savedRow array inside of jqGrid object,
// so it is safe to call restoreRow method with any id parameter
// if jqGrid not in editing state
if (typeof lastSel !== "undefined") {
grid.jqGrid('restoreRow', lastSel);
}
lastSel = id;
}
},
afterEditCell: function (rowid, cellname, value, iRow, iCol) {
alert(iCol);
},
gridComplete: function () {
},
loadComplete: function (data) {
}
})
.jqGrid('navGrid', '#pta_form_d', {}, editSettings, addSettings, delSettings,
{ multipleSearch: true, overlay: false,
onClose: function (form) {
// if we close the search dialog during the datapicker are opened
// the datepicker will stay opened. To fix this we have to hide
// the div used by datepicker
//$("div#ui-datepicker-div.ui-datepicker").hide();
}
});
$("#t_ta_form_d").append("<input type='button' class='add' value='Add New Allocation' style='height:20px; color:green; font-size:11px;' />");
$("input.add", "#t_ta_form_d").click(function () {
jQuery("#ta_form_d").jqGrid('editGridRow', "new", {
//recreateForm:true,
jqModal: false,
reloadAfterSubmit: false,
savekey: [true, 13],
closeOnEscape: true,
closeAfterAdd: true,
onclickSubmit: onclickSubmitLocal
})
})

In case of "local" datatype one can use footerData method to set (or get) the data in the footer. Additionally the method getCol can be used co calculate the sum of elements in the column.
Look at the answer for more information. I hope it will solve your problem.

Related

jqgrid save previous row onSelectRow not working

I'm using jqgrid 4.8.2. If a user clicks on a row, I want to save the previously selected row and display the current row in edit mode. I've used the code from previous answers but can't get this functionality to work. After some debugging and looking through the source code for jqgrid, it appears saveRow does not fire because the 'editable' attribute for the previous row is immediately set to 0. There is a clause in the jqgrid source code that looks for this attribute before saving the row:
editable = $(ind).attr("editable");
o.url = o.url || $t.p.editurl;
if (editable==="1") {....saveRow occurs...}
Any suggestion on how to work around this?
var editingRowId;
var myEditParam = {
keys: true,
extraparam: {activityID: aID},
oneditfunc: function (id) { editingRowId = id; },
afterrestorefunc: function (id) { editingRowId = undefined; },
aftersavefunc: function (id) { editingRowId = undefined; }
};
var myGrid = jQuery("#spend_grid").jqGrid({
url: parentGridURL,
mtype: "POST",
datatype: "json",
postData: {filters: myFilters},
gridview: false, //added for IE
altRows: true,
emptyrecords: 'NO PAYMENTS FOUND',
editurl: savePaymentURL,
onSelectRow: function (id) {
console.log('id=' + id + ' editingRowId =' + editingRowId);
var $this = $(this);
if (editingRowId !== id) {
if (editingRowId) {
// save or restore currently editing row
console.log('should be saving ' + editingRowId);
$this.jqGrid('saveRow', editingRowId, myEditParam);
console.log('done saving');
}
$this.jqGrid("editRow", id, myEditParam);
}
},
search: true,
sortname: lastSortName,
sortorder: lastSortOrder,
page: lastPage,
pager: jQuery('#report_pager'),
rowNum: lastRowNum,
rowList: [10,20,50,100],
viewrecords: true,
clearSearch: false,
caption: "Payments",
sortable: true,
shrinkToFit: false,
excel: true,
autowidth: true,
height: 300,
subGrid: true, // set the subGrid property to true to show expand buttons for each row
subGridRowExpanded: showChildGrid, // javascript function that will take care of showing the child grid
colModel: [
{ label: 'Payment ID',
name: 'PAYMENTID',
index: 'p.paymentID',
key: true, width: 75
},...// removed for brevity
{name : 'actions',
label: 'Actions',
index: 'formAction',
formatter:'actions',
width: 75,
search:false,
formatoptions: {
keys: true,
editbutton: false,
delOptions: { url: savePaymentURL }
}
}
]
}).inlineNav('#report_pager',
// the buttons to appear on the toolbar of the grid
{
restoreAfterSelect: false, // *****SOLUTION******
save:false,
edit: false,
add: false,
cancel: false
});
Found a solution to the issue. After examining the jqgrid source code I found that restoreAfterSelect: true is the default setting for the inlineNav. I added restoreAfterSelect: false and now the previous row saves.

jqGrid filterToolbar with local data

I have a jQgrid that loads data initially via an ajax call from backend(java struts). Again, this is one time load and once loaded, the jqGrid should operate on the data available locally.
Initially, datatype:json and once loadcomplete, set datatype:local.
Now is there a way to use filterToolbar for local datatype with the following options in free jqgrid;
autocomplete enabled in the toolbar
excel like filtering options
Jqgrid Options:
jQuery("#listTable").jqGrid({
url:'/WebTest/MainAction.do',
datatype: "json",
colNames: ['Label','Value'],
colModel: [
{name:'label',index:'label',width: 40,search:true, stype:'text',sorttype:'int'},
{name:'value',index:'value',width: 56,search:true, stype:'text',sorttype:'text'}
],
autowidth: true,
autoResizing: { compact: true, widthOfVisiblePartOfSortIcon: 13 },
rowNum: 10,
rowList: [5, 10, 20, "10000:All"],
viewrecords: true,
pager: true,
toppager: true,
rownumbers: true,
sortname: "label",
sortorder: "desc",
caption: "Test 235",
height: "200",
search: true,
loadonce: true,
loadComplete: function (data) {
},
gridComplete: function(){
jQuery("#listTable").jqGrid('setGridParam', { datatype: 'local' });
}
}) .jqGrid("navGrid", { view: true, cloneToTop: true})
.jqGrid("filterToolbar")
.jqGrid("gridResize");
All the features are enabled by default if I understand you correctly. The server just have to return all data instead of one page of data to make loadonce: true property work correctly. You need just call filterToolbar after creating the grid. All will work like with local data. You should consider to set sorttype property for correct local sorting and stype and searchoptions for correct filtering of data.
To have "autocomplete" and "excel like filtering options" you need additionally to follow the answer which set autocomplete or stype: "select", searchoptions: { value: ...} properties based on different values of input data. You can do this inside of beforeProcessing callback. The code from the answer use this.jqGrid("getCol", columnName) which get the data from the grid. Instead of that one have access to data returned from the server inside of beforeProcessing callback. So one can scan the data to get the lists with unique values in every column and to set either autocomplete or stype: "select", searchoptions: { value: ...} properties.
UPDATED: I created JSFiddle demo which demonstrates what one can do: http://jsfiddle.net/OlegKi/vgznxru6/1/. It uses the following code (I changed just echo URL to your URL):
$("#grid").jqGrid({
url: "/WebTest/MainAction.do",
datatype: "json",
colNames: ["Label", "Value"],
colModel: [
{name: "label", width: 70, template: "integer" },
{name: "value", width: 200 }
],
loadonce: true,
pager: true,
rowNum: 10,
rowList: [5, 10, "10000:All"],
iconSet: "fontAwesome",
cmTemplate: { autoResizable: true },
shrinkToFit: false,
autoResizing: { compact: true },
beforeProcessing: function (data) {
var labelMap = {}, valueMap = {}, i, item, labels = ":All", values = [],
$self = $(this);
for (i = 0; i < data.length; i++) {
item = data[i];
if (!labelMap[item.label]) {
labelMap[item.label] = true;
labels += ";" + item.label + ":" + item.label;
}
if (!valueMap[item.value]) {
valueMap[item.value] = true;
values.push(item.value);
}
}
$self.jqGrid("setColProp", "label", {
stype: "select",
searchoptions: {
value: labels,
sopt: ["eq"]
}
});
$self.jqGrid("setColProp", "value", {
searchoptions: {
sopt: ["cn"],
dataInit: function (elem) {
$(elem).autocomplete({
source: values,
delay: 0,
minLength: 0,
select: function (event, ui) {
var grid;
$(elem).val(ui.item.value);
if (typeof elem.id === "string" && elem.id.substr(0, 3) === "gs_") {
grid = $self[0];
if ($.isFunction(grid.triggerToolbar)) {
grid.triggerToolbar();
}
} else {
// to refresh the filter
$(elem).trigger("change");
}
}
});
}
}
});
// one should use stringResult:true option additionally because
// datatype: "json" at the moment, but one need use local filtreing later
$self.jqGrid("filterToolbar", {stringResult: true });
}
});

How to maintain checkbox selection while reloading the JQuery Grid?

I have a JQuery grid which I am reloading everytime when some event occurs on the server (i.e. update in the data set) and display the latest set of data in the grid. This grid has also got checkboxes in it's first column. What's happening is let's say user is selecting some checkboxes and in the meantime if the grid gets reloaded due to update in the data on the server, my grid gets reloaded with the latest set of data but all my previous checkbox selection gets lost. How can I mark these selected checkboxes again after grid reload?
Please suggest.
function PushData() {
// creates a proxy to the Alarm hub
var alarm = $.connection.alarmHub;
alarm.notification = function () {
$("#jqTable").trigger("reloadGrid",[{current:true}]);
};
// Start the connection and request current state
$.connection.hub.start(function () {
BindGrid();
});
}
function BindGrid() {
jqDataUrl = "Alarm/LoadjqData";
var selectedRowIds;
$("#jqTable").jqGrid({
url: jqDataUrl,
cache: false,
datatype: "json",
mtype: "POST",
multiselect: true ,
postData: {
sp: function () { return getPriority(); },
},
colNames: ["Id", "PointRef", "Value", "State", "Ack State", "Priority", "AlarmDate", "", ""],
colModel: [
//{ name: 'alarmId_Checkbox', index: 'chekbox', width: 100, formatter: "checkbox", formatoptions: { disabled: false }, editable: true, edittype: "checkbox" },
{ name: "AlarmId", index: "AlarmId", width: 70, align: "left" },
{ name: "PointRef", index: "PointRef", width: 200, align: "left" },
{ name: "Value", index: "Value", width: 120, align: "left" },
{ name: "AlarmStateName", index: "AlarmStateName", width: 150, align: "left" },
{ name: "AcknowledgementStateName", index: "AcknowledgementStateName", width: 200, align: "left" },
{ name: "Priority", index: "Priority", width: 130, align: "left" },
{ name: "AlarmDate", index: "AlarmDate", width: 250, align: "left" },
{ name: "TrendLink", index: "Trends", width: 100, align: "left" },
{ name: "MimicsLink", index: "Mimics", width: 100, align: "left" }
],
// Grid total width and height
width: 710,
height: 500,
hidegrid: false,
// Paging
toppager: false,
pager: $("#jqTablePager"),
rowNum: 20,
rowList: [5, 10, 20],
viewrecords: true, // "total number of records" is displayed
// Default sorting
sortname: "Priority",
sortorder: "asc",
// Grid caption
caption: "Telemetry Alarms",
onCellSelect: function (rowid, icol, cellcontent, e) {
var cm = jQuery("#jqTable").jqGrid('getGridParam', 'colModel');
var colName = cm[icol];
//alert(colName['index']);
if (colName['index'] == 'AlarmId') {
if ($("#AlarmDialog").dialog("isOpen")) {
$("#AlarmDialog").dialog("close");
}
AlarmDialogScript(rowid)
}
else if (colName['index'] == 'Trends') {
TrendDialogScript(rowid)
}
else if (colName['index'] == 'Mimics') {
MimicsDialogScript(rowid)
}
else {
$("#jqTable").setCell(rowid, "alarmId_Checkbox", true); //Selects checkbox while clicking any other column in the grid
}
},
recreateFilter: true,
emptyrecords: 'No Alarms to display',
loadComplete: function () {
var rowIDs = jQuery("#jqTable").getDataIDs();
for (var i = 0; i < rowIDs.length; i++) {
rowData = jQuery("#jqTable").getRowData(rowIDs[i]);
//change the style of hyperlink coloumns
$("#jqTable").jqGrid('setCell', rowIDs[i], "AlarmId", "", { 'text-decoration': 'underline', 'cursor': 'pointer' });
$("#jqTable").jqGrid('setCell', rowIDs[i], "TrendLink", "", { 'text-decoration': 'underline', 'cursor': 'pointer' });
$("#jqTable").jqGrid('setCell', rowIDs[i], "MimicsLink", "", { 'text-decoration': 'underline', 'cursor': 'pointer' });
if ($.trim(rowData.AcknowledgementStateName) == 'Active') {
$("#jqTable").jqGrid('setCell', rowIDs[i], "AcknowledgementStateName", "", { 'background-color': 'red', 'color': 'white' });
}
else if ($.trim(rowData.AcknowledgementStateName) == 'Acknowledged') {
$("#jqTable").jqGrid('setCell', rowIDs[i], "AcknowledgementStateName", "", { 'background-color': 'Navy', 'color': 'white' });
}
}
//$("#jqTable").jqGrid('hideCol', "AlarmId") //code for hiding a particular column
},
gridComplete: function () {
$('#jqTable input').bind('mouseover', function () {
var tr = $(this).closest('tr');
if ($("#AlarmDialog").dialog("isOpen")) {
$("#AlarmDialog").dialog("close");
}
AlarmDialogScript(tr[0].id);
}
);
}
}).navGrid("#jqTablePager",
{ refresh: true, add: false, edit: false, del: false },
{}, // settings for edit
{}, // settings for add
{}, // settings for delete
{sopt: ["cn"]} // Search options. Some options can be set on column level
)
.trigger('reloadGrid', [{page:1, current:true}]);
}
If I understand you correct you need just use additional parameter i
$("#list").trigger("reloadGrid", [{current:true}]);
or
$("#list").trigger("reloadGrid", [{page: 1, current: true}]);
(See the answer). It do almost the same what Justin suggested.
Depend from what you want exactly mean under reloading of the grid it can be that waht you need is to save the grid state inclusive the list of selected items in localStorage and reload the state after loading the grid. This answer describes in details the implementation. The corresponding demo from the answer could be simplified using new jqGrid events which are introduced in version 4.3.2.
You could save the selections before reloading the grid, for example in the beforeRequest event:
selectedRowIDs = jQuery('#myGrid').getGridParam('selarrrow');
Then after the grid has been reloaded, you can loop over selectedRowIDs and reselect each one using setSelection. For example:
for (i = 0, count = selectedRowIDs.length; i < count; i++) {
jQuery('#myGrid').jqGrid('setSelection', selectedRowIDs[i], false);
}
You might run this code in the loadComplete event.
use
recreateFilter: true
and you should be done

jqgrid apply filter after reload?

Am using jqgrid, loading data once, sorting and filtering locally and doing a refresh after each update/insert/delete. It works fine, besides that if I am using a filter (on top of grid), the filter remains the same after refresh, but the filter is not re-applied on the newly loaded data.
I have tried to call mygrid[0].triggerToolbar() after reloading grid with trigger('reloadGrid'), but there is no effect.
Thanks for any help or pointers :-)
Code below:
var lastsel;
var mygrid;
var currentLang;
//setup grid with columns, properties, events
function initGrid(lang, productData, resellerData, resellerSearchData) {
mygrid = jQuery("#list2").jqGrid({
//data loading settings
url: '/public/Gadgets/LinkGadget/ProductLinks/' + lang,
editurl: "/public/Gadgets/LinkGadget/Edit/" + lang,
datatype: "json",
mtype: 'POST',
jsonReader: {
root: "rows",
cell: "",
page: "currpage",
//total: "totalrecords",
repeatitems: false
},
loadError: function (xhr, status, error) { alert(status + " " + error); },
//column definitions
colNames: ['Id', 'ProductId', 'Reseller Name', 'Link', 'Link Status'],
colModel: [
{ name: 'Id', index: 'Id', width: 40, sortable: false, resizable: true, editable: false, search: false, key: true, editrules: { edithidden: true }, hidden: true },
{ name: 'ProductId', index: 'ProductId', width: 190, sortable: true, sorttype: 'text', resizable: true, editable: true, search: true, stype: 'select', edittype: "select", editoptions: { value: productData }, editrules: { required: true} },
{ name: 'ResellerName', indexme: 'ResellerName', width: 190, sortable: false, sorttype: 'text', resizable: true, editable: true, search: true, stype: 'select', edittype: "select", editoptions: { value: resellerData }, editrules: { required: true }, searchoptions: { sopt: ['eq'], value: resellerSearchData} },
{ name: 'Link', index: 'Link', width: 320, sortable: true, sorttype: 'text', resizable: true, editable: true, search: true, edittype: "textarea", editoptions: { rows: "3", cols: "50" }, editrules: { required: true }, searchoptions: { sopt: ['cn']} },
{ name: 'LinkStatus', index: 'LinkStatus', width: 100, sortable: false, resizable: true, editable: false, search: false, formatter: linkStatusFormatter}],
//grid settings
//rowList: [10, 25, 50],
rowNum: 20,
pager: '#pager2',
sortname: 'ProductId',
sortorder: 'asc',
height: '100%',
viewrecords: true,
gridview: true,
loadonce: true,
viewsortcols: [false, 'vertical', true],
caption: " Product links ",
//grid events
onSelectRow: function (id) {
if (id && id !== lastsel) {
if (lastsel == "newid") {
jQuery('#list2').jqGrid('delRowData', lastsel, true);
}
else {
jQuery('#list2').jqGrid('restoreRow', lastsel);
}
jQuery('#list2').jqGrid('editRow', id, true, null, afterSave); //reload on success
lastsel = id;
}
},
gridComplete: function () {
//$("#list2").setGridParam({ datatype: 'local', page: 1 });
$("#pager2 .ui-pg-selbox").val(25); //changing the selected values triggers paging to work for some reason
}
});
//page settings
jQuery("#list2").jqGrid('navGrid', '#pager2',
{ del: false, refresh: false, search: false, add: false, edit: false }
);
//refresh grid button
jQuery("#list2").jqGrid('navButtonAdd', "#pager2", { caption: "Refresh", title: "Refresh grid", buttonicon: 'ui-icon-refresh',
onClickButton: function () {
reload();
}
});
//clear search button
jQuery("#list2").jqGrid('navButtonAdd', "#pager2", { caption: "Clear search", title: "Clear Search", buttonicon: 'ui-icon-refresh',
onClickButton: function () {
mygrid[0].clearToolbar();
}
});
//add row button
jQuery("#list2").jqGrid('navButtonAdd', '#pager2', { caption: "New", buttonicon: 'ui-icon-circle-plus',
onClickButton: function (id) {
var datarow = { Id: "newid", ProductId: "", ResellerName: "", Link: "" };
jQuery('#list2').jqGrid('restoreRow', lastsel); //if editing other row, cancel this
lastsel = "newid"; // id;
var su = jQuery("#list2").addRowData(lastsel, datarow, "first");
if (su) {
jQuery('#list2').jqGrid('editRow', lastsel, true, null, afterSave); //reload on success
jQuery("#list2").setSelection(lastsel, true);
}
},
title: "New row"
});
//delete row button
jQuery("#list2").jqGrid('navButtonAdd', "#pager2", { caption: "Delete", title: "Delete row", buttonicon: 'ui-icon-circle-minus',
onClickButton: function () {
if (lastsel) {
if (confirm('Are you sure you want to delete this row?')) {
var url = '/public/Gadgets/LinkGadget/Edit/' + currentLang;
var data = { oper: 'del', id: lastsel };
$.post(url, data, function (data, textStatus, XMLHttpRequest) {
reload();
});
lastsel = null;
}
else {
jQuery("#list2").resetSelection();
}
}
else {
alert("No row selected to delete.");
}
}
});
jQuery("#list2").jqGrid('filterToolbar');
}
//Used by initGrid - formats link status column by adding button
function linkStatusFormatter(cellvalue, options, rowObject) {
if (rowObject.Id != "newrow")
return "<input style='height:22px;width:90px;' type='button' " + "value='Check link' " + "onclick=\"CheckLink(this, '" + rowObject.Id + "'); \" />";
else
return "";
}
//Used by initGrid - reloads grid
function reload() {
//jqgrid setting "loadonce: true" will reset datatype from json to local after grid has loaded. "datatype: local"
// does not allow server reloads, therefore datatype is reset before trying to reload grid
$("#list2").setGridParam({ datatype: 'json' }).trigger('reloadGrid');
//mygrid[0].clearToolbar();
lastsel = null;
//Comments: after reload, toolbar filter is not applied to refreshed data. Tried row below without luck
//mygrid[0].triggerToolbar();
}
//after successful save, reload grid and deselect row
function afterSave() {
reload();
}
Your data is not getting re binded to the grid after save/update delete

jqGrid with Virtual Scrolling not returning records in correct order

We're using a jqGrid 3.8.2 with virtual scrolling to scroll a list of records. It works well if the user is scrolling through the list slowly. As soon as the user grabs the scroll bar and quickly scrolls all the way to the bottom the records returned in the grid are completely out of order and sometimes duplicated. The AJAX call we are making to get the records calls a WCF service that returns the records from a database to the AJAX callback in the jqGrid in which the data then gets loaded to the grid. The database expects a page number which in turn indicates which page of records we want.
I am pretty sure the results are coming back in the wrong order due to timing of the AJAX calls. Meaning the calls aren't always first in first out. Newer calls are likely being returned back to the jqGrid before earlier calls.
Any thoughts on how to get this to work correctly? Any steps that I could take to help get us going in the right direction?
If it helps, here is the js configuration:
PersonalListContactGridControl.GetGridConfig = function() {
var jqGridConfig = {
url: Common.AjaxService.serviceUrl + "/GetPersonalListContacts",
datatype: "JSON",
ajaxGridOptions: { contentType: "application/json; charset=utf-8" },
mtype: "POST",
autowidth: true,
height: PersonalListContactGridControl.Constants.Height,
altRows: true,
sortname: "LastName",
viewrecords: true,
emptyrecords: "",
loadtext: '',
multiselect: true,
//sortorder: "desc",
//- virtual scrolling:: 1 ON / 0 OFF
loadonce: false, // SUPPORT FOR REBIND
rowNum: PersonalListContactGridControl.Constants.GridPageSize,
scroll: 1,
gridview: true,
//- column header text
colNames: ['EndPointID',
PersonalListContactGridControl.Constants.FirstNameColumnDisplayText,
PersonalListContactGridControl.Constants.LastNameColumnDisplayText,
PersonalListContactGridControl.Constants.EmailFaxColumnDisplayText,
PersonalListContactGridControl.Constants.TypeColumnDisplayText],
//- column specific definitions
colModel: [
{ name: 'EndPointID', index: 'EndPointID', hidden: true },
{ name: 'FirstName', index: 'FirstName', width: 155, align: 'left', editable: false, title: true,
hidden: false, resizable: true, sortable: true, editoptions: { readonly: true },
formatter: PersonalListContactGridControl.formatLink },
{ name: 'LastName', index: 'LastName', width: 155, align: 'left', editable: false, title: true,
hidden: false, resizable: true, sortable: true, editoptions: { readonly: true },
formatter: PersonalListContactGridControl.formatLink },
{ name: 'EmailAddress', index: 'EmailAddress', width: 240, align: 'left', editable: false, title: true,
hidden: false, resizable: true, sortable: true, editoptions: { readonly: true },
formatter: PersonalListContactGridControl.formatEmailFax },
{ name: 'EndPointType', index: 'EndPointType', width: 60, align: 'left', editable: false, title: true,
hidden: false, resizable: true, sortable: true, editoptions: { readonly: true },
formatter: PersonalListContactGridControl.formatType }
],
//- jqGrid's "reader" ... the structure of the JSON/XML returned (Fiddler) must match this
jsonReader: {
root: "d.Contacts",
total: function(obj) {
return Math.ceil(obj.d.TotalRecords / PersonalListContactGridControl.Constants.GridPageSize);
},
records: function(obj) {
return obj.d.TotalRecords;
},
id: 'EndPointID',
repeatitems: false
},
serializeGridData: function(jqGridParms) {
//alert(jqGridParms.page);
return PersonalListContactGridControl.GetRequest(jqGridParms);
},
loadComplete: function(data) {
if (data.d) {
if (data.d.ResponseProperties.Success === false) {
if (data.d.ResponseProperties.ErrorMessage.indexOf('The List has been deleted') > -1) {
ManagePersonalList.OnPersonalListErrorHandler();
return;
}
}
var grid = PersonalListContactGridControl.Grid();
if (data.d.Contacts == null || data.d.Contacts.length == 0) { // are there any records?
// check if row exists
if (!grid.getInd(-1)) {
if (PersonalListContactGridControl.SearchString == "") {
grid.addRowData(-1, { "FirstName": PersonalListContactGridControl.Constants.NoContactDisplayText });
}
else {
grid.addRowData(-1, { "FirstName": PersonalListContactGridControl.Constants.NoContactForSearchDisplayText });
}
grid.find('#-1 input').hide(); // takeout the selection checkbox
}
PersonalListGridControl.ToggleExportButton(false);
}
else {
if (PersonalListContactGridControl.Constants.dtoToSelectID != null) {
if (PersonalListContactGridControl.SelectContactById(PersonalListContactGridControl.Constants.dtoToSelectID, true))
PersonalListContactGridControl.Constants.dtoToSelectID = null;
}
PersonalListGridControl.ToggleExportButton(true);
}
// cache the return objects
PersonalListContactGridControl.DTOS = PersonalListContactGridControl.DTOS.concat(data.d.Contacts);
PersonalListContactGridControl.InitRows();
}
},
loadError: function(xhr, status, error) {
alert("Type: " + status + "; Response: " + xhr.status + " " + xhr.statusText);
},
onCellSelect: function(rowid, iCol, cellcontent, e) {
if (iCol == 0)
PersonalListContactGridControl.CheckItem();
else
PersonalListContactGridControl.SelectContactById(rowid);
},
onSelectRow: function(id, status) {
if (!status) return;
},
onSelectAll: function() {
var grid = PersonalListContactGridControl.Grid();
var selections = new Array();
selections = grid.getGridParam('selarrrow');
if (selections[0] == -1) {
//if -1, then no rows are available
PersonalListContactGridControl.UpdateHeaderCount(0);
}
else {
PersonalListContactGridControl.UpdateHeaderCount(selections.length);
ManagePersonalList.OnSelectAllChanged(this, { Ids: selections });
}
}
};
return jqGridConfig;
}
Try changing your JSONReader to..
jsonReader: {
root: "d.Contacts",
page: "d.currpage",
total: "d.totalpages",
records: "d.TotalRecords",
id: 'EndPointId',
repeatitems: false
},
the d.totalpages and d.currpage should come back from the AJAX call

Resources