jqplot replot using dateaxisrenderer goes blank - jqplot

I am trying to use JQPlot for plotting a live time based data .
As it is live data, I am doing an ajax call every 10 seconds to get new data and appending it to the existing dataset before replotting.
The initial plotting of the JQPlot chart is fine but subsequently from the first replot onwards the chart becomes blank for ever.
Here is my javascript code.
$.jqplot.config.enablePlugins = false;
var Graph;
var GraphUpdate;
var GraphData = [];
var interval = 10000;
var npoints = 25;
var maxIterations = 200;
var niters = 0;
var fetchInProgress = false;
var lastSuccessCDR = 0;
function BuildDataArray() {
GraphData = [];
lastSuccessCDR = 0;
if(fetchInProgress == false)
{
postdata = 'successCDR='+lastSuccessCDR;
fetchInProgress = true;
$.ajax({url:'/CDR-Analyser/php/livedata_fetch.php',
type:'POST',
data:postdata,
async:true,
dataType:"json",
success: function(data,status){
if(lastSuccessCDR == 0)
{
//GraphData = [data[0]['data']];
GraphData = [[["2013-07-17 21:11:20",2],["2013-07-17 21:12:20",5],["2013-07-17 21:14:20",7]]];
//GraphData = [[[1,2],[2,5],[3,7]]];
lastSuccessCDR = data[1]['lastCDR'];
Graph = $.jqplot('livechart', GraphData, {
stackseries : true,
seriesDefaults: {
showMarker: false,
fill: true,
fillAndStroke: true
},
axes: {
xaxis: {
//numberTicks:2,
//renderer:$.jqplot.DateAxisRenderer,
//pad:0,
renderer:$.jqplot.DateAxisRenderer,
tickOptions: {
angle: -30
}
},
yaxis: {
label: 'Call Count',
//min:0,
//max:30,
tickInterval:2,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer
}
},
cursor:{
zoom:true,
looseZoom: true
}
});
}
fetchInProgress = false;
}
});
}
}
function UpdateDataArray() {
var set = 0;
if(fetchInProgress == false)
{
postdata = 'successCDR='+lastSuccessCDR;
fetchInProgress = true;
$.ajax({url:'/CDR-Analyser/php/livedata_fetch.php',
type:'POST',
data:postdata,
async:true,
dataType:"json",
success: function(data,status){
set = [data[0]['data']];
lastSuccessCDR = data[1]['lastCDR'];
var newData = new Array();
newData = GraphData;
newData[0].shift();
newData[0].push(set[0][0]);
GraphData = [["2013-07-17 21:11:20",2],["2013-07-17 21:12:20",5]];
//Graph.series[0].data = newData[0];
Graph.series[0].data = [["2013-07-17 21:11:20",2],["2013-07-17 21:12:20",5]];
//Graph.data[0] = [["2013-07-17 21:12:20",5],["2013-07-17 21:14:20",7]] ;
Graph.replot({resetAxes:true});
fetchInProgress = false;
}
});
}
return set;
}
function StartGraphLoop() {
BuildDataArray();
GraphUpdate = setInterval(UpdateGraph, interval);
}
function UpdateGraph() {
UpdateDataArray();
}
function StopGraphLoop() {
clearInterval(GraphUpdate);
}
I have two functions above , BuildDataArray() and updateDataArray() for building the initial data set and for getting the subsequent data elements.
As you can see I have replaced the actual data received from ajax ( it is commented) with sample data to test the behavour of JQPlot. Unfortunately with this sample data also the chart goes blank at the call of first replot. I had earlier tested this with sample data without the dates ( something like [[1,2] , [2,5] , [3,4]] ) by commenting out the dateaxisrenderer option in xaxis property.
So now I am left with no option so either I am missing something or it is a bug in JQPlot while rendering dateaxis.
Please advise

I had the same problem using the dateAxisRenderer where the series were being updated and the plot redrawn but there was no data in my graph. After a lot of head scratching I found this stackoverflow answer
Instead of updating the series data via
chartObj.series[i].data = // your data
You can create a variable and pass it as an argument to the replot() function. To use the snippet from C5H8NNaO4 as an example
var series = [[1,2],[2,3]];
chartObj.replot({data:series});
I haven't tested it with your code but it looks like we had the same problem and this solved the issue for me. If this works I suggest you up-vote the initial answer too :)

Related

DC js charts, crossfilter not filtering on click

*Updated with more relevant code.
*Updated again: removing chart groupings results in this error: "Unable to get property 'classed' of undefined or null reference dc.js (5575,9)". I am using dc 3.0.11.
I have an issue where my dc charts are not filtering each other upon clicking a selection on a chart. It does work if I call a function to explicitly do so, but I would like to avoid that.
This is my general approach:
I do have dc.css included (if that matters)
my crossfilter ndx and dimensions are correct
I am defining my charts WITHOUT .on renderlet (is this the reason?)
var masterData = [];
$(document).ready(function () { //UPDATED CODE START
var siteurl = 'site';
var ItemCount= GetItemCount(siteurl, 'list');
createRestUrl(siteurl,ItemCount,'list');
}); // UPDATED CODE END
function GetItemCount(siteurl, ListName){
var ItemCount='';
$.ajax({
url: siteurl+"/_api/web/lists/GetByTitle('"+ListName+"')/ItemCount",
method: "GET",
async: false,
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
ItemCount = data.d.ItemCount;
},
error: function (data) {
console.log(data);
}
});
return ItemCount;
} // GetItemCount END
function createRestUrl(siteurl, ItemCount, ListName) {
var listServiceUrl = siteurl+ "/_api/web/lists/GetByTitle('" + ListName + "')/Items";
//Rest call to process each items of list
$.when(processList(listServiceUrl,ItemCount)).done(function () {
console.log("FINISHED");
console.log("--------");
console.log(masterData);
var ndx = crossfilter(masterData),
clientMgr = ndx.dimension(function(d) { return d.clientMgr}),
otherTeammates = ndx.dimension(function(d) { return d.otherTeammates}),
topic = ndx.dimension(function(d) { return d.topic}),
status = ndx.dimension(function(d) { return d.status}),
team = ndx.dimension(function(d) { return d.team}),
requester = ndx.dimension(function(d) { return d.requester}),
requesterBusiness = ndx.dimension(function(d) { return d.requesterBusiness}),
submitted = ndx.dimension(function(d) { return d.submitted}),
clientMgrGroup = clientMgr.group(),
otherTeammatesGroup = otherTeammates.group(),
topicGroup = topic.group(),
statusGroup = status.group(),
teamGroup = team.group(),
requesterGroup = requester.group(),
requesterBusinessGroup = requesterBusiness.group(),
submittedGroup = submitted.group();
var teamChart = dc.rowChart("#team_chart", "team");
var clientMgrChart = dc.pieChart("#mgr_chart", "mgr");
var statusChart = dc.pieChart("#status_chart", "status");
var requesterChart = dc.rowChart("#requester_chart", "request");
var requesterBusinessChart = dc.pieChart("#requesterBusiness_chart", "requestBusiness");
var timeSelect = dc.lineChart("#time_chart", "time");
var topicChart = dc.pieChart("#topic_chart", "topic");
var teamNum = dc.numberDisplay("#teamNum", "teamNum");
Date.prototype.addDays = function(days) {
var dat = new Date(this.valueOf());
dat.setDate(dat.getDate() + days);
return dat;
}
var thisDay = new Date();
var todayMinSix = thisDay.addDays(-30);
var todayPlusSix = thisDay.addDays(30);
teamChart
.dimension(team)
.group(teamGroup)
.width(800)
.height(200)
.transitionDuration(3000)
.ordering(function(d) { return -d.value })
.elasticX(true)
.x(d3.scaleLinear().domain([0, 100]))
.colors('#58D3F7')
//teamNum
//.valueAccessor(function(d) { return d})
//.formatNumber(d3.format())
//.group(teamGroup)
clientMgrChart
.dimension(clientMgr)
.group(clientMgrGroup)
.width(300)
.height(300)
.transitionDuration(3000)
statusChart
.dimension(status)
.group(statusGroup)
.height(200)
.width(500)
.innerRadius(95)
.transitionDuration(3000)
.colors(d3.scaleOrdinal().domain(["02 - Work-In-Progress", "01 - Pending Initial Review"])
.range(['#58D3F7', '#2E9AFE']))
requesterChart
.dimension(requester)
.group(requesterGroup)
.height(200)
.width(800)
.gap(10)
.transitionDuration(3000)
.ordering(function(d) { return -d.value })
.elasticX(true)
.colors('#F78181')
.x(d3.scaleLinear().domain([0, 100]));
requesterBusinessChart
.dimension(requesterBusiness)
.group(requesterBusinessGroup)
.height(300)
.width(300)
.innerRadius(117)
.transitionDuration(3000)
.colors('#F78181')
topicChart
.dimension(topic)
.group(topicGroup)
.height(200)
.width(500)
.innerRadius(95)
.transitionDuration(3000)
.colors(d3.scaleOrdinal().domain(["New Report / Interface", "General Support", "One-Time Data Set", "Recurring Data Set", "Modify Existing Report / Interface", "Production Issue"])
.range(['#F5A9A9', '#F78181', '#FA5858', '#F6CECE', '#F8E0E0', "#FBEFEF"]))
timeSelect
.width(1700)
.height(150)
.dimension(submitted)
.group(submittedGroup)
.transitionDuration(1000)
.elasticY(true)
.renderArea(true)
.x(d3.scaleTime().domain([todayMinSix, thisDay]))
.xUnits(d3.timeDays)
.mouseZoomable(false)
.xAxis();
teamChart.render();
statusChart.render();
requesterChart.render();
topicChart.render();
});
}
//Step 3: Rest call to process each items of list
function processList(nextUrl,ItemCount) {
var dfd = new $.Deferred();
if (nextUrl == undefined) {
dfd.resolve();
return;
}
getJSONDataFromUrl(nextUrl).done(function (listItems) {
TotalItemCount = TotalItemCount+listItems.d.results.length;
console.log("getJSON called");
var items = listItems.d.results;
var next = listItems.d.__next;
$.when(processList(next,ItemCount)).done(function (){
dfd.resolve();
});
$.each(items, function(index, obj){
items = {};
items.clientMgr = obj.ClientMgr; //Assigned To - might not be the right field
items.otherTeammates = obj.OtherTeammatesEngaged; //Assigned To - might not be the right field
items.topic = obj.Topic;
items.status = obj.Status;
items.team = obj.Team;
items.requester = obj.RequesterLOB;
items.submitted = obj.Submitted;
items.requesterBusiness = obj.RequesterBusinessGroup;
console.log("looping through each");
var convert = new Date(items.submitted);
items.submittedConvert = moment(convert).format('L');
items.submitted = convert;
var assignName = items.clientMgr;
items.clientMgr = assignName;
masterData.push(items);
console.log(items.requesterBusiness);
console.log(items.status);
}); //.each END
}); // getJSONDataFromUrl END
return dfd.promise();
} // processList END
I had this working long ago, but as the project became more complex, something broke along the way.
I'm going to guess that the error refers to the use of .classed() a few lines up from 5575. Here are the lines leading up to 5575:
if (d3.sum(_chart.data(), _chart.cappedValueAccessor)) {
pieData = pie(_chart.data());
_g.classed(_emptyCssClass, false);
} else {
// otherwise we'd be getting NaNs, so override
// note: abuse others for its ignoring the value accessor
pieData = pie([{key: _emptyTitle, value: 1, others: [_emptyTitle]}]);
_g.classed(_emptyCssClass, true);
}
if (_g) {
https://github.com/dc-js/dc.js/blob/3.0.11/dc.js#L5565-L5575
So (except for the line number being a little off) it's a good guess that _g is null.
As I noted in my comment above, this probably indicates that the chart was redrawn before it was rendered. This can happen if you create a chart but don't render it. Rendering initializes things like the scale and parent elements like the <g> that holds the chart.
When you create a chart, you either specify a chart group or the default chart group is selected for you. The chart is registered in that group, and when any chart in the group is filtered, all the charts are redrawn.
In your code above, you render specific charts (4 of them), but there are many more charts which you initialize but don't render (8). In particular, all of the pie charts are rendered except for clientMgrChart. When you later click on a chart, it's likely that chart crashes when it tries to redraw.
It would be nice if dc.js implemented a more helpful error for this case since you currently have to guess "hmm, null, guess something hasn't been set up right" and know that render must happen before redraw.
Note: it's more robust to initialize the charts and then call
dc.renderAll();
instead of rendering each one manually.
Note to others: With dc.js 3.1.2, my individual rowCharts would not animate themselves when clicked. The other charts in the crossfilter did animate.
Very confused, traced all the way through the dc.js call stack and learned a lot.
Ultimately, root cause for the failure was not having dc.css linked in my page's HTML.
Another good 'tell' that you don't have dc.css properly linked is that the rowChart vertical lines do not render.
Screenshots
rowChart With CSS - Clicking any of the bars DOES trigger animation.
rowChart Without CSS - Clicking any of the bars does not trigger animation.

Removing appended isotope items

I'm appending isotope items via Ajax in Wordpress:
My JS Code:
var $news_container = $('#news'); //The ID for the list with all the blog posts
$news_container.isotope({ //Isotope options, 'item' matches the class in the PHP
itemSelector: '.newsItem',
masonry: {
columnWidth: '.news-item-sizer',
gutter: '.gutter-sizer'
}
});
var has_run = false;
var init_offset = 0;
$('button.showall').click(function(e) {
e.preventDefault();
var button = $(this);
// Disable button
$(button).removeClass('showall');
$(button).addClass('showless');
// Record Nonce
var nonce = $(this).data("nonce");
if(has_run == false) {
button.data('offset', $(this).data("offset"));
init_offset = $(this).data("offset");
}
// Set AJAX parameters
data = {
action: 'mft_load_more_ajax',
init_offset: init_offset,
offset: button.data('offset'),
nonce: nonce
};
$.post(mft_load_more_ajax.ajaxurl, data, function(response) {
// Set Container Name
var response = JSON.parse(response);
console.log(response);
// Run through JSON
$.each( response, function( key, value ) {
// Set Value
var val = $(value);
// Set Container
var $container = $('#news').isotope();
// Append Val
$container.append(val).isotope( 'appended', val );
$(button).html('show less');
});
// Set Offset
var offset = button.data("offset");
button.data("offset", offset + 11 );
// If Has Run
has_run = true;
return false;
}
Until now, this works quite fine. Now I would like to switch the buttontext and it's class to .showless and on the next click all previously appended items should be removed. They all have the class .newsItem.appendedItem.
I tried this method:
$('button.showless').click(function(e) {
var button = $(this);
console.log('showless');
$out = $('.newsItem.appendedItem');
var isotopeInstance = $('#news').data('isotope');
isotopeInstance.$allAtoms = isotopeInstance.$allAtoms.not($out);
$out.remove();
// Disable button
$(button).removeClass('showless');
$(button).addClass('showall');
has_run = false;
return false;
});
Unfortunately this doesn't work, because the showless function is not even being entered, as I don't get a log in the console. What am I overlooking?
Thanks for your help!
Cara
Update 1:
I'm getting this error in Google Console.
Not sure, what was the problem now. But I cleaned the code a little bit with using an if statement, to check if the elements already got appended.
So my final code now looks like this:
var $news_container = $('#news'); //The ID for the list with all the blog posts
$news_container.isotope({ //Isotope options, 'item' matches the class in the PHP
itemSelector: '.newsItem',
masonry: {
columnWidth: '.news-item-sizer',
gutter: '.gutter-sizer'
}
});
var is_appended = false;
$('button.showall').click(function(e) {
e.preventDefault();
var button = $(this);
// Record Nonce
var nonce = $(this).data("nonce");
if(is_appended == false) {
button.data('offset', $(this).data("offset"));
// Disable button
button.prop( "disabled" , true );
// Set AJAX parameters
data = {
action: 'mft_load_more_ajax',
offset: button.data('offset'),
nonce: nonce
};
$.post(mft_load_more_ajax.ajaxurl, data, function(response) {
// Set Container Name
var response = JSON.parse(response);
console.log(response);
// Run through JSON
$.each( response, function( key, value ) {
// Set Value
var val = $(value);
// Set Container
var $container = $('#news').isotope();
// Append Val
$container.append(val).isotope( 'appended', val );
});
// Undo Button Disable
button.prop( "disabled" , false );
button.html('Weniger News anzeigen');
// Set Offset
// var offset = button.data("offset");
// button.data("offset", offset + 11 );
// If Was appended
is_appended = true;
return false;
});
} else if(is_appended == true) {
$out = $('.newsItem.appendedItem');
$news_container.isotope( 'remove', $out )
// layout remaining item elements
.isotope('layout');
button.html('Alle News anzeigen');
is_appended = false;
return false;
}
});
In Chrome everything works perfectly. But just figured out, that in Firefox my Ajax array is completely empty and I'm not able to fetch any data. Probably another problem, I'm going to post separately.

SlickGrid filter not working

I am fairly new to SlickGrid. I am trying to make SlickGrid filter work but no luck. I am following the example (http://mleibman.github.io/SlickGrid/examples/example4-model.html).
Below is my source code.
$(document).ready(function() {
var tName;
$('#submit').click(function(e) {
tName = $('#source option:selected').text();// name1
tName = tName.trim();
$.ajax({
url : 'someUrl',
type : 'GET',
cache : false,
success : function(d) {
var grid;
var searchString = "";
var data = [];
var columns = new Array();
var cols = d[0].columns;
var pkColNames = d[0].pkColNames;
for (var j=0; j< cols.length; j++) {
var key = {id: cols[j].colName, name: cols[j].colName, field: cols[j].colName, width: 200, sortable:true, editor: Slick.Editors.LongText};
columns[j] = key;
}
var options = {
editable: true,
enableAddRow: false,
enableCellNavigation: true,
asyncEditorLoading: false,
enableColumnReorder:true,
multiColumnSort: false,
autoEdit: false,
autoHeight: false
};
function myFilter(item, args) {
return true;// Let us return true all time ?
}
for (var i = 0; i < d.length; i++) {
var tempData = (data[i] = {});
var title = null;
var val = null;
for (var q = 0; q < d[i].columns.length; q++) {
title = d[i].columns[q].colName;
val = d[i].columns[q].colValue;
tempData[title] = val;
}
}
var dataView = new Slick.Data.DataView({ inlineFilters: true });
grid = new Slick.Grid("#myGrid", dataView, columns, options);
dataView.setPagingOptions({
pageSize: 25
});
var pager = new Slick.Controls.Pager(dataView, grid, $("#myPager"));
var columnpicker = new Slick.Controls.ColumnPicker(columns, grid, options);
grid.setSelectionModel(new Slick.CellSelectionModel());
grid.onAddNewRow.subscribe(function(e, args) {
// Adding a new record is not yet decided.
});
grid.onCellChange.subscribe(function (e) {
var editedCellNo = arguments[1].cell;
var editedColName = grid.getColumns()[editedCellNo].field;
var newUpdatedValue= arguments[1].item[grid.getColumns()[editedCellNo].field];
var editedColType = "";
for (var cnt = 0; cnt < cols.length; cnt++) {
if (editedColName == cols[cnt].colName) {
editedColType = cols[cnt].colType;
break;
}
}
var pkKeyValue="";
for (var v=0; v <pkColNames.length;v++) {
for (var p=0; p<grid.getColumns().length; p++) {
if (pkColNames[v] == grid.getColumns()[p].field) {
var value = arguments[1].item[grid.getColumns()[p].field];
pkKeyValue += "{"+pkColNames[v] + '~' +getColDbType(grid.getColumns()[p].field) + ":"+value+"},";
break;
}
}
}
function getColDbType(colName) {
for (var c = 0; c < cols.length; c++) {
if (colName == cols[c].colName) {
return cols[c].colType;
}
}
}
pkKeyValue = pkKeyValue.substring(0, pkKeyValue.length-1);
$.ajax({
url: 'anotherUrl',
type:'GET',
dataType:'text',
success: function(f) {
bootbox.alert("Data updated successfully");
},
error: function() {
bootbox.alert("Error - updating data. Please ensure you are adding the data in right format.");
grid.invalidateAllRows();
grid.render();
}
});
});
grid.onClick.subscribe(function (e) {
//do-nothing
});
dataView.onRowsChanged.subscribe(function(e, args) {
grid.updateRowCount();
grid.invalidateRows(args.rows);
grid.render();
});
grid.onSort.subscribe(function(e, args) {
// args.multiColumnSort indicates whether or not this is a multi-column sort.
// If it is, args.sortCols will have an array of {sortCol:..., sortAsc:...} objects.
// If not, the sort column and direction will be in args.sortCol & args.sortAsc.
// We'll use a simple comparer function here.
var comparer = function(a, b) {
return a[args.sortCol.field] > b[args.sortCol.field];
}
// Delegate the sorting to DataView.
// This will fire the change events and update the grid.
dataView.sort(comparer, args.sortAsc);
});
// wire up the search textbox to apply the filter to the model
$("#txtSearch").keyup(function (e) {
console.log('Called...txtSearch');
Slick.GlobalEditorLock.cancelCurrentEdit();
// clear on Esc
if (e.which == 27) {
this.value = "";
}
searchString = this.value;
updateFilter();
});
function updateFilter() {
console.log("updateFilter");
dataView.setFilterArgs({
searchString: searchString
});
dataView.refresh();
}
dataView.beginUpdate();
dataView.setItems(data, pkColNames);
dataView.setFilterArgs({
searchString: searchString
});
dataView.setFilter(myFilter);
dataView.endUpdate();
},
error : function() {
bootbox.alert("Invalid user");
}
});
});
});
Your function myFilter() is always returning true so of course it will never work. The example that you looked at, was to filter something specific. I would recommend that you look at the following example to have a generic filter. From the example, simply type the text you are looking on a chosen column and look at the result... see example below (from SlickGrid examples).Using fixed header row for quick filters
In case you want a more in depth conditional filters ( > 10, <> 10, etc...), please take a look at my previous answer on how to make this kind of filtering possible, see my previous answer below:SlickGrid column type
Hope that helps you out

Adding a .ajaxForm function to standard .ajax call

So I'm trying to find a method of getting a progress bar working on my .ajax call but not having much luck. I know that the ajaxForm plugin has the following code in it that allows for the uploadProgress option but the way my code works I'm not able to use that plugin. Is there anyway of adding the following code somehow so that it attaches to the standard .ajax call? Long shot I know!
// XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
function fileUploadXhr(a) {
var formdata = new FormData();
for (var i=0; i < a.length; i++) {
formdata.append(a[i].name, a[i].value);
}
if (options.extraData) {
var serializedData = deepSerialize(options.extraData);
for (i=0; i < serializedData.length; i++)
if (serializedData[i])
formdata.append(serializedData[i][0], serializedData[i][1]);
}
options.data = null;
var s = $.extend(true, {}, $.ajaxSettings, options, {
contentType: false,
processData: false,
cache: false,
type: method || 'POST'
});
if (options.uploadProgress) {
// workaround because jqXHR does not expose upload property
s.xhr = function() {
var xhr = jQuery.ajaxSettings.xhr();
if (xhr.upload) {
xhr.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position; /*event.position is deprecated*/
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
options.uploadProgress(event, position, total, percent);
}, false);
}
return xhr;
};
}
s.data = null;
var beforeSend = s.beforeSend;
s.beforeSend = function(xhr, o) {
o.data = formdata;
if(beforeSend)
beforeSend.call(this, xhr, o);
};
return $.ajax(s);
}
I do not guarantee on that, but try this:
xhr.upload.onprogress = function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
console.log("Progress: "+percent+"%"); //debug to see if the problem is there
options.uploadProgress(event, position, total, percent);
};
From posted code, I cannot even guess if the problem is in onprogress event handling or that options.uploadProgress, whatever it is.
I posted this as answer only because it wouldn't fit in comment.

How to highlight the last selected row after client-side sorting on jqGrid?

I've a jqGrid-based application, and I use loadonce: true in the grid option and sortable: true, sorttype: 'text in the colModel to allow client-side sorting on the data grid. However, I found that, once the data grid is re-sorted, the last selected row will no longer be highlighted. My question is, how to keep the selected row being highlighted across data resorting?
I prepared for you the small demo which keeps the row selection. In the demo I rewrote the code of selectionPreserver used in case of reloadGrid usage having additional parameters: $("#list").trigger("reloadGrid", [{current:true}]);. See the answer for details.
The demo saves the current selection inside of the onSortCol event handler and restore it inside of the loadComplete:
onSortCol: function () {
saveSelection.call(this);
},
loadComplete: function () {
restoreSelection.call(this);
}
How you see the usage is very simple and you can integrate it in your code. The implementation of the saveSelection and restoreSelection is the following:
var lastSelArrRow = [],
lastScrollLeft = 0,
lastSelRow = null,
saveSelection = function () {
var $grid = $(this);
lastSelRow = $grid.jqGrid('getGridParam', 'selrow');
lastSelArrRow = $grid.jqGrid('getGridParam', 'selrow');
lastSelArrRow = lastSelArrRow ? $.makeArray(lastSelArrRow) : null;
lastScrollLeft = this.grid.bDiv.scrollLeft;
},
restoreSelection = function () {
var p = this.p,
$grid = $(this);
p.selrow = null;
p.selarrrow = [];
if (p.multiselect && lastSelArrRow && lastSelArrRow.length > 0) {
for (i = 0; i < lastSelArrRow.length; i++) {
if (lastSelArrRow[i] !== lastSelRow) {
$grid.jqGrid("setSelection", lastSelArrRow[i], false);
}
}
lastSelArrRow = [];
}
if (lastSelRow) {
$grid.jqGrid("setSelection", lastSelRow, false);
lastSelRow = null;
}
this.grid.bDiv.scrollLeft = lastScrollLeft;
};

Resources