jqGrid is sorting only in ascending order in version 4.4.3 - jqgrid

This is my code. When I click on asc or desc it always sorts data in ascending order only. Can you please let me know, do I need to add anything?
if (typeof jQuery("#plist")[0] != UNDEF && jQuery("#plist")[0].grid) {
jQuery("#plist").GridUnload();
}
jQuery("#plist").jqGrid({
url: URL,
datatype: 'json',
mtype: 'POST',
colModel: colModelData,
pager: '#pagerTrade',
rowNum: 80,
gridview: true,
rowList: [80, 160, 240],
viewrecords: true,
height: "550",
width: "auto",
viewsortcols: [true, 'vertical', true],
multiselect: true,
onSortCol: function(index, columnIndex, sortOrder) {
//alert("index:"+index+", columnIndex:" +columnIndex + ", sortOrder:" +sortOrder);
var colSort = {};
colSort.index = getColumnIndex("plist", columnIndex);
colSort.direction = sortOrder;
var colSortList = [ colSort ];
var pageNo = getPageNo("plist");
var rowCount = getRowCount("plist");
//Function CAll()
return 'stop';
},
gridComplete: function() {
}
});
Server side code:
if(!CommonUtil.isNullOrEmpty(request.getParameter("sidx"))){
sortByColumn = request.getParameter("sidx");
}
if(!CommonUtil.isNullOrEmpty(request.getParameter("sord"))){
sortByOrder = request.getParameter("sord");
}

You use datatype: 'json' without loadonce: true option. It means that there server (see url : URL) is responsible for sorting, paging and filtering/searching of data. If the user click on the sorting icon then jqGrid just send one more request to the server using sidx and sord parameters, which corresponds the clicked column. I guess that your server code ignores sord parameter and the server just returns the page of data sorted in ascending order only. You need fix you server code or to change your code to use use loadonce: true option.

Your onSortCol event return always stop, which causes the grid not to send the needed data to your server and your sort is always not performed (populate is not executed).
onSortCol: function(index, columnIndex, sortOrder) {
//alert("index:"+index+", columnIndex:" +columnIndex + ", sortOrder:" +sortOrder);
var colSort = {};
colSort.index = getColumnIndex("plist", columnIndex);
colSort.direction = sortOrder;
var colSortList = [ colSort ];
var pageNo = getPageNo("plist");
var rowCount = getRowCount("plist");
//Function CAll()
return 'stop'; <===== HERE STOP RETURNED
},
To prewent this just remove the return stop and you will get your parameters at server

Related

What causes jqgrid events to fire multiple times?

Our jqgrid is configured in an initgrid function that is called as the last statement of a ready handler. For some reason, the gridcomplete function is getting called multiple times. With the code below, it gets called twice, but it had been getting called 3 times. Twice is bad enough. After stepping through it multiple times, I don't see what is triggering the second execution of the gridComplete function.
When I hit the debugger at the start of gridComplete, the call stack is virtually identical each time, the only difference being a call to 'L' in the jqgrid:
Anyone have an idea why this is occurring? We are using the free version 4.13, in an ASP.net MVC application.
$(function(){
....
initGrid();
}
function initGrid(){
$gridEl.jqGrid({
xhrFields: {
cors: false
},
url: "/IAConsult/GetWorkFlowIARequests",
postData: {
showAll: showAllVal,
role: role,
IsIAArchitect: userIsIA
},
datatype: "json",
crossDomain: true,
loadonce: true,
mtype: 'GET',
sortable: true,
viewrecords: true,
pager: '#workFlowIAGridPager',
multiselect: true,
rowNum: 50,
autowidth: true,
colModel: [...],
beforeSelectRow: function (rowid, e) {
var $myGrid = $(this),
i = $.jgrid.getCellIndex($(e.target).closest('td')[0]),
cm = $myGrid.jqGrid('getGridParam', 'colModel');
return (cm[i].name === 'cb');
},
jsonReader: {
repeatitems: true,
root: "IAConsultWorkflowRequestsList"
},
beforeSubmitCell: function (rowid, name, value, iRow, iCol) {
return {
gridData: gridData
};
},
serializeCellData: function (postdata) {
return JSON.stringify(postdata);
},
gridComplete: function () {
console.log('grid complete');
let rowIDs = $gridEl.getDataIDs();
let inCompleteFlag = false;
let dataToFilter = $gridEl.jqGrid('getGridParam', 'lastSelectedData').length == 0
? $gridEl.jqGrid("getGridParam", "data")
: $gridEl.jqGrid('getGridParam', 'lastSelectedData');
let $grid = $gridEl, postfilt = "";
let localFilter = $gridEl.jqGrid('getGridParam', 'postData').filters;
let columnNames = columns.split(',');
$('.moreItems').on('click', function () {
$.modalAlert({
body: $(this).data('allitems'),
buttons: {
dismiss: {
caption: 'Close'
}
},
title: 'Design Participants'
});
});
rowCount = $gridEl.getGridParam('records');
gridViewRowCount = rowCount;
let getUniqueNames = function (columnName) {
... };
let buildSearchSelect = function (uniqueNames) {
var values = {};
values[''] = 'All';
$.each(uniqueNames,
function () {
values[this] = this;
});
return values;
};
let setSearchSelect = function (columnName) {
...
};
function getSortOptionsByColName(colName) {
...
}
$grid.jqGrid("filterToolbar",
{ stringResult: true, searchOnEnter: true });
if (localFilter !== "" && localFilter != undefined) {
globalFilter = localFilter;
}
let grid = $gridEl.jqGrid("setGridParam",
{
postData: {
"filters": globalFilter,
showAll: showAllVal,
role: role,
IsIAArchitect: userIsIA
},
search: true,
forceClientSorting: true
});
//grid.trigger("reloadGrid");
//Ending Filter code
for (i = 0; i < columnNames.length; i++) {
var htmlForSelect = '<option value="">All</option>';
var un = getUniqueNames(columnNames[i]);
var $select = $("select[id='gs_workFlowIAGrid_" + columnNames[i] + "']");
for (j = 0; j < un.length; j++) {
val = un[j];
htmlForSelect += '<option value="' + val + '">' + val + '</option>';
}
$select.find('option').remove().end().append(htmlForSelect);
}
debugger;
},
// all grid parameters and additionally the following
loadComplete: function () {
$gridEl.jqGrid('setGridWidth', $(window).width(), true);
$gridEl.setGridWidth(window.innerWidth - 20);
},
height: '100%'
});
I personally almost never use gridComplete callback. It exists in free jqGrid mostly for backwards compatibility. I'd recommend you to read the old answer, which describes differences between gridComplete and loadComplete.
Some additional advices: it's dangerous to register events inside of callbacks (see $('.moreItems').on('click', ...). If you need to make some actions on click inside of grid then I'd recommend you to use beforeSelectRow. Many events, inclusive click event supports event bubbling and non-handled click inside of grid will be bubbled to the parent <table> element. You use already beforeSelectRow callback and e.target gives you full information about clicked element.
I recommend you additionally don't use setGridParam method, which can decrease performance. setGridParam method make by default deep copy of all internals parameters, inclusive arrays like data, which can be large. In the way, changing one small parameter with respect of setGridParam can be expensive. If you need to modify a parameter of jqGrid then you can use getGridParam without additional parameters to get reference to internal object, which contains all jqGrid parameters. After that you can access to read or modify parameters of jqGrid using the parameter object. See the answer for example for small code example.
Also adding the property
loadonce: true
might help.

free-jqGrid 4.15.6 ExpandNode producing runtime error

I recently upgraded from the original Tony Tomov's jqGrid v4.5.4 to Oleg's free-jqGrid 4.15.6.
When using 4.5.4, the code below worked perfect, but in 4.15.6, it does not. The run-time error is produced by the two calls to expandNode and expandRow located in the forceNodeOpen() function. The error given:
TypeError: rc1 is undefined
The forceNodeOpen() function is used to force all ancestor nodes of the current treegrid node to show as expanded. Much like a table of contents...if we load an initial topic node, we want the whole topic hierarchy to be expanded:
// force a node (if expandable) to stay expanded
function forceNodeOpen(rowid)
{
var record = $("#tree").jqGrid('getRowData', rowid);
var div = $('tr#'+rowid).find('div.ui-icon.treeclick');
div.removeClass('ui-icon-triangle-1-e tree-plus').addClass('ui-icon-triangle-1-s tree-minus');
**$('#tree').jqGrid('expandNode', record);**
**$('#tree').jqGrid('expandRow', record);**
// get all ancestoral parents and expand them
// *NOTE*: the getAncestorNodes function of grid was not usable for
// some reason as the same code below just would not work
// with the return array from getAncestorNodes
var parent = $("#tree").jqGrid('getNodeParent', record);
while(parent)
{
forceNodeOpen(parent['id']);
parent = $("#tree").jqGrid('getNodeParent', parent);
}
}
// using topic url, get the tree row id
function getTopicID(topic)
{
var nodes = $('#tree').jqGrid('getRowData');
var rowid = 1;
$.each(nodes, function(e,i)
{
var url = $(this).attr('url');
if(url == topic)
{
rowid = $(this).attr('id');
return false;
}
});
return rowid;
}
// post request to help server via ajax
function loadTopic(topic)
{
// no need to load again
if(loadedtopic == topic) { return false; }
// select the topic node
var rowid = getTopicID(topic);
loading = true;
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
loading = false;
// wipe content
$('h1#help_content_topic span:first').html('Loading...');
$('div#help_content').html('');
// block UI for ajax posting
blockInterface();
// request help content
$.ajax(
{
type: 'POST',
url: '/index.php',
data: { 'isajax': 1, 'topic': topic },
success: function(data)
{
$.unblockUI();
$('h1#help_content_topic span:first').html(data['topic']);
$('div#help_content').html(data['content']);
return false;
}
});
// save current topic to prevent loading same topic again
loadedtopic = topic;
}
// table of contents
$('#tree').jqGrid({
url: "topics.php",
datatype: "xml",
autowidth: true,
caption: "Help Topics",
colNames: ["id","","url"],
colModel: [
{name: "id",width:1,hidden:true, key:true},
{name: "topic", width:150, resizable: false, sortable:false},
{name: "url",width:1,hidden:true}
],
ExpandColClick: true,
ExpandColumn: 'topic',
gridview: false,
height: 'auto',
hidegrid: false,
pager: false,
rowNum: 200,
treeGrid: true,
treeIcons: {leaf:'ui-icon-document-b'},
// auto-select topic node
gridComplete: function()
{
// save current topic to prevent loading same topic again
loadedtopic = '<? echo($topic) ?>';
var rowid = getTopicID('<? echo($topic) ?>');
$('#tree').jqGrid('setSelection', rowid);
forceNodeOpen(rowid);
$.unblockUI();
},
// clear initial loading
loadComplete: function()
{
loading = false;
},
onSelectRow: function(rowid)
{
// ignore initial page loads
if(loading) { return false; }
// load the selected topic
var topic = $("#tree").jqGrid('getCell',rowid,'url');
loadTopic(topic);
}
});
The forceNodeOpen(rowid) is invoked from the loadTopic() function, which is called inside the onSelectRow() event of the treegrid.
Not sure what 4.5.4 did that allowed this code to work but 4.15.6 finds it to be an error. The offending line in 4.15.6.src.js:
expandNode: function (rc) {
...
if (p.treedatatype !== "local" && !base.isNodeLoaded.call($($t), p.data[p._index[id]]) && !$t.grid.hDiv.loading) {
// set the value which will be used during processing of the server response
// in readInput
p.treeANode = rc1.rowIndex;
p.datatype = p.treedatatype;
...});
I have only included a few lines from the above core function. It's the p.treeANode = rc1.rowIndex that throws the error.
I have to be missing something but do not know what. Hoping somebody can tell me what to do. If I remark out the two expandNode and expandRow treegrid function calls in forceNodeOpen() function, the system does not error out and the desired topic loads. But the hierarchy is not expanded as desired.
START EDIT 1
The server-side code that returns the topic nodes:
echo("<?xml version='1.0' encoding='UTF-8'?>\n");
require('db.php');
echo("<rows>\n");
echo("<page>1</page>\n");
echo("<total>1</total>\n");
echo("<records>1</records>\n");
$sql = "SELECT node.id, node.parentid, node.topic, node.url, node.lft, node.rgt, (COUNT(node.parentid) - 1) AS depth
FROM helptopics AS node, helptopics AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.url ORDER BY node.lft";
$stmt = AMDB::selectStatement($sql);
while($data = $stmt->fetch(PDO::FETCH_ASSOC))
{
$id = $data['id'];
$pid = $data['parentid'];
$topic = $data['topic'];
$url = $data['url'];
$lft = $data['lft'];
$rgt = $data['rgt'];
$leaf = $rgt - $lft == 1 ? 'true' : 'false';
$exp = 'false';
$dep = $data['depth'];
echo("<row><cell>{$id}</cell><cell>{$topic}</cell><cell>{$url}</cell><cell>{$dep}</cell><cell>{$lft}</cell><cell>{$rgt}</cell><cell>{$leaf}</cell><cell>{$exp}</cell></row>\n");
}
echo("</rows>\n");
exit();
END EDIT 1
I see that you use
var record = $("#tree").jqGrid('getRowData', rowid);
to get node data of TreeGrid. It wasn't good, but it worked in old jqGrid because it saved internal jqGrid data twice: once as local data and once more time in hidden cells of the grid.
You should use getLocalRow instead of getRowData:
var record = $("#tree").jqGrid('getLocalRow', rowid);
The code will work on both old jqGrid and free jqGrid. Additionally, getLocalRow has always better performance comparing with getRowData even in jqGrid 4.5.4.

jqGrid local filtering causing rowid to be set to null

Based on jqGrid Filtering Records solution, i implemented the filtering on local.
However i am unable to set the first row as selected.Looks the row ids have been set to null. I am not sure why this would happening.
Code is as below
$("#showMatchingRecords").click(function(e) {
e.preventDefault();
var grid = $("#WorkList"), filter,searchfiler;
searchfiler = "700677";
grid[0].p.search = true;
filter = { groupOp:'OR', rules: [] };
filter.rules.push({field:"Id",op:"eq",data:searchfiler});
$.extend(grid[0].p.postData, {filters:JSON.stringify(filter)});
grid.trigger("reloadGrid", [{page:1}]);
});
$("#showAllRecords").click(function(e) {
e.preventDefault();
var grid = $("#WorkList");
grid[0].p.search = false;
$.extend(grid[0].p.postData, { filters: "" });
grid.trigger("reloadGrid", [{page:1}]);
});
Here is my gridComplete method, I have tried the same with loadComplete as well
gridComplete : function() {
$("tr.jqgrow:odd").css("background", "#DDDDDC");
var grid = $("#WorkList");
var ids = grid.getDataIDs();
console.log('before ids....', ids);
for (var i = 0; i < ids.length; i++) {
grid.setRowData(ids[i], false, { height: 35 });
}
Here are my grid options:
caption: 'Records List',
width: 1000,
height: 200,
pager: $("#WorkListPager"),
rowNum: 50,
sortname: 'Name',
loadonce: true,
sortorder: "asc",
cellEdit: false,
viewrecords: true,
imgpath: '/Content/Themes/Redmond/Images',
autowidth: false,
onSelectRow: function (rowid, status, e) {
/* function */
}
To select the first row on every reload of the grid you need do the following
loadComplete: function () {
var $self = $(this);
if (this.rows.length > 1) { // test that grid is not empty
$self.jqGrid("setSelection", this.rows[1].id);
}
}
I strictly recommend you additionally to include gridview: true option (see the answer for details) in the grid and to replace gridComplete to the usage of rowattr (see the answer).

jqGrid: Form edits saved to row but all changes lost when paging back or forth

I am using form editing for local data. I am able to edit the values in the form and set the values back to the row (using setRowData). But when I page back or forth, the changes are lost.
How do I save the changes to the row and the underlying source in the grid? Later I have to iterate the rows, validate all the errors are corrected (using the edit form), and post it to server.
Code:
var gridId = 'mygrid';
var pagerId = 'mygridpager';
var grid = $('#mygrid');
var pager = $('#mygridpager');
grid.jqGrid({
caption: caption,
colModel: getColModel(),
recreateForm: true,
hidegrid: true,
sortorder: 'desc',
viewrecords: true,
multiselect: true,
rownumbers: true,
autowidth: true,
height: '100%',
scrollOffset: 0,
datatype: 'local',
data: dataAsArray,
localReader: {
repeatitems: true,
cell: '',
id: 2
},
pager: '#' + pagerId,
pgbuttons: true,
rowNum: 5,
rowList: [2, 5, 7, 10, 250, 500]
});
grid.jqGrid('navGrid',
'#' + pagerId,
{
add: false,
del: false,
search: false,
edit: true,
edittext: 'Fix Error',
editicon: 'ui-icon-tag',
editurl: 'clientArray',
refreshtext: 'Refresh',
recreateForm: true
},
{
// edit options
editCaption: 'Fix Error',
editurl: 'clientArray',
recreateForm: true,
beforeShowForm: function(form) {
/* Custom style for elements. make it disabled etc */
},
onclickSubmit: function(options, postdata) {
var idName= $(this).jqGrid('getGridParam').prmNames.id;
// [UPDATED]
if (postdata[idName] === undefined) {
var idInPostdata = this.id + "_id";
postdata[idName] = postdata[idInPostdata];
postdata['row'] = postdata[idInPostdata];
}
$('#mygrid').jqGrid('setRowData', postdata.row, postdata);
if (options.closeAfterEdit) {
$.jgrid.hideModal('#editmod' + gridId, {
gb: '#gbox_' + gridId,
jqm: options.jqModal,
onClose: options.onClose
});
}
options.processing = true;
return {};
}
},
{}, // add options
{}, // del options
{} //search options
).trigger('reloadGrid');
Your help is appreciated.
Thanks
I suppose that the reason of your problem is usage of array format (repeatitems: true in localReader) of input data. I suppose that you need build array from postdata and use it as the parameter of setRowData instead of postdata.
If the advice will don't help you that you should post more full code of the grid with the test data. The code like colModel: getColModel(), don't really help. In other words you should post enough information to reproduce the problem. The best would be a working demo in http://jsfiddle.net/. If you prepare such demo please use jquery.jqGrid.src.js instead of jquery.jqGrid.min.js.
UPDATED: The problem in your code is the usage arrays is items if input data (you use repeatitems: true in localReader). The current implementation of setRowData don't support (works incorrect) in the case. For example if you have such data initially
and start editing of the third row you will have something like the following
after usage the like $('#mygrid').jqGrid('setRowData', postdata.row, postdata); inside of onclickSubmit. So the internal data will be incorrectly modified.
To fix the problem I suggest overwrite the current implementation of setRowData by including the following code
$.jgrid.extend({
setRowData : function(rowid, data, cssp) {
var nm, success=true, title;
this.each(function(){
if(!this.grid) {return false;}
var t = this, vl, ind, cp = typeof cssp, lcdata=t.p.datatype === "local" && t.p.localReader.repeatitems === true? [] : {}, iLocal=0;
ind = $(this).jqGrid('getGridRowById', rowid);
if(!ind) { return false; }
if( data ) {
try {
$(this.p.colModel).each(function(i){
nm = this.name;
var dval =$.jgrid.getAccessor(data,nm);
if( dval !== undefined) {
vl = this.formatter && typeof this.formatter === 'string' && this.formatter === 'date' ? $.unformat.date.call(t,dval,this) : dval;
if (t.p.datatype === "local" && t.p.localReader.repeatitems === true) {
lcdata[iLocal] = vl;
} else {
lcdata[nm] = vl;
}
vl = t.formatter( rowid, dval, i, data, 'edit');
title = this.title ? {"title":$.jgrid.stripHtml(vl)} : {};
if(t.p.treeGrid===true && nm === t.p.ExpandColumn) {
$("td[role='gridcell']:eq("+i+") > span:first",ind).html(vl).attr(title);
} else {
$("td[role='gridcell']:eq("+i+")",ind).html(vl).attr(title);
}
}
if (nm !== "cb" && nm !== "subgrid" && nm !== "rn") {
iLocal++;
}
});
if(t.p.datatype === 'local') {
var id = $.jgrid.stripPref(t.p.idPrefix, rowid),
pos = t.p._index[id], key;
if(t.p.treeGrid) {
for(key in t.p.treeReader){
if(t.p.treeReader.hasOwnProperty(key)) {
delete lcdata[t.p.treeReader[key]];
}
}
}
if(pos !== undefined) {
t.p.data[pos] = $.extend(true, t.p.data[pos], lcdata);
}
lcdata = null;
}
} catch (e) {
success = false;
}
}
if(success) {
if(cp === 'string') {$(ind).addClass(cssp);} else if(cssp !== null && cp === 'object') {$(ind).css(cssp);}
$(t).triggerHandler("jqGridAfterGridComplete");
}
});
return success;
}
});
I will post my suggestion later to trirand. So one can hopes that the problem will be fixed in the next version of jqGrid.

scroll to jqgrid item by id/key

I've a jqgrid width multiple pages. Every row has an unique id. Is there a function to scroll directly i.e. to row id 1234?
var mygrid = $("#list").jqGrid({
url: 'get_sql_json.php',
datatype: "json",
mtype: "GET",
pager: "#pager",
rowNum: 100,
rowList: [100, 200, 500],
autoencode: true,
autowidth: true,
sortorder: "desc",
shrinkToFit: true,
viewrecords: true,
loadonce: true,
gridview: true,
autoencode: true
});
I find your question very interesting, so I prepared the demo which demonstrates the corresponding solution.
One can choose the row in the dropdown-box and click the button "Select row by id". The specified row will be selected with setSelection method if it is on the current page. If the row in on another page then it will be calculated new page number and the grid will be reloaded with the specified row pre-selected. I use current: true option of reloadGrid described here.
It's important that you use loadonce: true which allows to save more as one page of rows. In the case the grid works like the grid having datatype: "local" with data parameter defined with the data returned from the server at the first load.
For the implementation of the selection I use the approach described in the answer. I overwrite (subclass) the select method of internal $.jgrid.from method used by jqGrid so that the last full sorted and filtered local data are saved in new internal variable lastSelected. It allows to access the full dataset instead of the current page only (the contains the data from the line instead of the current page only after execution of slice from the line).
The most important part of the code of the demo is below:
var mydata = [ ... ],
$grid = $("#list"),
oldFrom = $.jgrid.from,
lastSelected, i, n, $ids, id;
// "subclass" $.jgrid.from method and save the last
// select results in lastSelected variable
$.jgrid.from = function (source, initalQuery) {
var result = oldFrom.call(this, source, initalQuery),
old_select = result.select;
result.select = function (f) {
lastSelected = old_select.call(this, f);
return lastSelected;
};
return result;
};
// create the grid
$grid.jqGrid({
datatype: "local",
data: mydata,
...
loadComplete: function () {
this.p.lastSelected = lastSelected; // set this.p.lastSelected
}
});
...
// fill select with all ids from the data
$ids = $("#selectedId");
for (i = 0, n = mydata.length; i < n; i++) {
id = mydata[i].id;
$("<option>").val(id).text(id).appendTo($ids);
}
$("#selectId").button().click(function () {
var filteredData = $grid.jqGrid("getGridParam", "lastSelected"), j, l,
idName = $grid.jqGrid("getGridParam", "localReader").id,
idToSelect = $("#selectedId").val(),
rows = $grid.jqGrid("getGridParam", "rowNum"),
currentPage = $grid.jqGrid("getGridParam", "page"),
newPage;
if (filteredData) {
for (j = 0, l = filteredData.length; j < l; j++) {
if (String(filteredData[j][idName]) === idToSelect) {
// j is the 0-based index of the item
newPage = Math.floor(j / rows) + 1;
if (newPage === currentPage) {
$grid.jqGrid("setSelection", idToSelect);
} else {
// set selrow or selarrrow
if ($grid.jqGrid("getGridParam", "multiselect")) {
$grid.jqGrid("setGridParam", {
page: newPage,
selrow: idToSelect,
selarrrow: [idToSelect]
});
} else {
$grid.jqGrid("setGridParam", {
page: newPage,
selrow: idToSelect
});
}
$grid.trigger("reloadGrid", [{current: true}]);
break;
}
}
}
if (j >= l) {
$grid.jqGrid("resetSelection");
alert("The id=" + idToSelect + " can't be seen beacuse of the current filter.");
}
}
});
You can select an ID using setSelection to scroll to that row, but only if it is on the current page.

Resources