0

I have an application which polls a database and updates the jqGrid at runtime. I am using datatype: "local" in order to have the ability to manipulate the data on the client- side without having to reload everything.

My first attempt updated that data itself, updated the grid's data and reloaded it. This worked, but in IE8 (our main target unfortunately) there is a flicker when the scrollbar is reset to its original position. There is also the issue that the selections are reset, but that one would be resolvable.

grid.setGridParam({ data: localData });
var scrollPosition = grid.closest(".ui-jqgrid-bdiv").scrollTop();
grid.trigger('reloadGrid');
grid.closest(".ui-jqgrid-bdiv").scrollTop(scrollPosition);

My second attempt updates the individual rows themselves depending on the operation.

if (toUpdate) { /* Not auto sorted */
   grid.jqGrid('setRowData', entityUpdate.EntityId, entityUpdate);
}
else if(toAdd) { /* Not auto sorted */
   grid.jqGrid('addRowData', entityUpdate.EntityId, entityUpdate);
}
else if(toDelete) {
   grid.jqGrid('delRowData', entityUpdate.EntityId);
}

This works great. The selections are not reset, there is no flicker, however there is one last issue: the rows are not resorted.

Any row that is updated stays where it is, and any row that is added does not go in the right place. I can use the "sortGrid" method, but then we're back to refreshing the whole grid. I can use a combination of the "position" and "srcrowid" parameters of the addRowData method, in order to place it in the right place, but I would have to know exactly where to put it. Is there a way to use the built in sorting algorithms to find where to put it? The code would become:

if (toUpdate) { 
   grid.jqGrid('delRowData', entityUpdate.EntityId);
   grid.jqGrid('addRowData', entityUpdate.EntityId, entityUpdate, ?, ?);
}
else if(toAdd) {
   grid.jqGrid('addRowData', entityUpdate.EntityId, entityUpdate, ?, ?);
}
else if(toDelete) {
   grid.jqGrid('delRowData', entityUpdate.EntityId);
}
helios456
  • 1,624
  • 16
  • 24
  • I added additional reference to [the answer](http://stackoverflow.com/a/10461964/315935) in my answer on your question. – Oleg Jun 15 '12 at 19:56

2 Answers2

2

I would recommend you to use

grid.trigger('reloadGrid', [{current:true}]);

to save the selection (see here). Depend on the other option of the grid you could not really need to save and to restore the scrollTop position. I suppose that the problem with scrolling could be the reason of flicker.

Moreover it's very important to verify that you use gridview: true option. Moreover the usage of datatype: "local" could be not the best solution at all. Probably you need just use datatype: "json" together with loadonce: true. You can update the data from the server if needed. See here and here for details.

It's very important to understand, that if you change one element (like a cell of the grid) on the page the web browser have to recalculate position of all existing elements. I recommend you to read the article about this. The current implementation of setRowData is so that html content of every cell of the row will be done separately (see here). So if you have n columns in the grid the web browser makes about n times reflow of the whole page. If you you addRowData the whole row will be made at one operation (like here).

The main advantage of the reloading the whole grid (with .setGridParam({ data: localData }); and reloadGrid) is that the whole grid body will be inserted as one operation (see here). So the update of the whole grid follows only one reflow. Of cause there are some other changes (like updating of the pageer), but in general updating of the whole page is much more quickly as it looks like at the first look. It's very important to use gridview: true to have the behavior.

UPDATED: I recommend you additionally to read the answer. Probably it can be helpful for you too.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • Unfortunately, the scrolling fix is still required using current:true. The reason is that when the grid is reloaded, as I understand it, the grid is emptied then repopulated, and the scroll goes back to the top. The flicker is indeed caused by this (IE8 only, works in Chrome). As for the retrieving the data from the server for every update as suggested in "answer", the flicker issue not withstanding, there is also the issue that I have multiple grids containing quite a bit of data, and would like to limit the traffic (only updates get sent). That is the reason I use local data. – helios456 Jun 18 '12 at 13:09
  • @helios456: Do you tried to remove `width` option of jqGrid (one can use another setting `height: "auto"` additionally)? In the case the scroll bar will not exist on `.ui-jqgrid-bdiv` and the problem will be looks other. – Oleg Jun 18 '12 at 18:59
  • Setting height:auto might work, but it would break the look and feel of the page, as I set the height of the grid as the height of the window and allow it to scroll within itself. – helios456 Jun 20 '12 at 14:40
0

I have resolved this by applying my own sorting. My columns are only of sorttype: 'text', therefore this works. As a future feature of the jqGrid, it would be nice if addRowData's position parameter would take in 'sorted', in order to specify that is goes in the sorted order. Please note that EntityId is my id column (a hidden column), and that my column options specify a sortname and sortorder when the grid is initially loaded.

function updateGrid(grid, entities) {
    var sortName = grid.jqGrid('getGridParam', 'sortname');
    var sortOrder = grid.jqGrid('getGridParam', 'sortorder');
    var rowData = grid.jqGrid('getRowData');
    for (var i = 0; i < entities.length; i++) {
        var entityUpdate = entities[i];
        if (shouldBeInGrid) {
             var currentRow = grid.jqGrid('getRowData', entityUpdate.EntityId);
            //The row exists, update it.
            if (!isEmptyRow(currentRow)) {
                if (currentRow[sortName] == entityUpdate[sortName]) {
                    //The column hasn't changed, just update the values.
                    grid.jqGrid('setRowData', entityUpdate.EntityId, entityUpdate);
                }
                else {
                    grid.jqGrid('delRowData', entityUpdate.EntityId);
                    addRowSorted(grid, rowData, sortName, sortOrder, entityUpdate);
                }
            }
            //The row does not exist, add it.
            else {
                 addRowSorted(grid, rowData, sortName, sortOrder, entityUpdate);
            }
        }
        //The row should not be in the table, delete it if it doesn't exist.
        else {
            grid.jqGrid('delRowData', entityUpdate.EntityId);
        }
    }
}

function addRowSorted(grid, allData, sortName, sortOrder, toAdd) {
    for (var i = 0; i < allData.length; i++) {
        var valueFromGrid = allData[i][sortName].toLowerCase();
        var valueToVerify = toAdd[sortName].toLowerCase();
        var srcId = allData[i][entityIdColumnId];
        if (sortOrder == "desc" && valueFromGrid < valueToVerify) {
            grid.jqGrid('addRowData', toAdd.EntityId, toAdd, 'before', srcId);
            return;
        }
        else if (sortOrder == "asc" && valueFromGrid > valueToVerify) {
            grid.jqGrid('addRowData', toAdd.EntityId, toAdd, 'before', srcId);
            return;
        }
    }
    //The data is empty or it should be last, add it at the end.
    grid.jqGrid('addRowData', toAdd.EntityId, toAdd, 'last');

}


function isEmptyRow(data) {
    for (var property in data) {
        return false;
    }
    return true;
}
helios456
  • 1,624
  • 16
  • 24